---
tags: Linux Kernel
---
# 2022q1 Homework (quiz15)
contributed by < [steven1lung](https://github.com/steven1lung) >
## `do_sift_up`
```c
static void do_sift_up(unsigned long idx,
unsigned long nr_items,
struct heap_item *h)
{
assert(idx > 0);
if (idx == 1)
return;
uint64_t v = h[idx].key;
unsigned long pidx = parent(idx);
uint64_t pv = h[pidx].key;
if (pv < v)
return;
struct heap_item tmp = h[pidx];
h[pidx] = h[idx];
h[idx] = tmp;
do_sift_up(pidx, nr_items, h);
}
```
這個函式所做的事情就是確認父節點是小於(或大於,看是 min-heap 還是 max-heap)當前的節點,如果是的話就不用變更,如果不是就要將父節點跟現在的節點互換。
因為往上互換後,換完節點的新的父節點又有可能會不符合 heap,所以要繼續檢查,故在最後一行又呼叫 `do_sift_up(pidx, nr_items, h)`。
### 拿掉遞回
依照剛剛的想法,我們可以確認要繼續做遞迴的條件就是「父節點大於子節點」,所以我們可以使用一個 `while` 迴圈只要符合上述的條件就進行互換。
```c
static void do_sift_up(unsigned long idx,
unsigned long nr_items,
struct heap_item *h)
{
assert(idx > 0);
unsigned long pidx = parent(idx);
while(pidx>=1 && h[pidx].key > h[idx].key)
{
struct heap_item tmp = h[pidx];
h[pidx] = h[idx];
h[idx] = tmp;
idx = pidx;
pidx = parent(idx);
}
}
```
在 `while` 迴圈裡要有上述的條件外(父節點大於子節點),還要確認我們所找的父街點是合法的,所以要多加一個判斷式 `parent(idx>=1`。
---
### 修改缺陷
:warning: 在[討論區](https://hackmd.io/@sysprog/H1AlR9PU9)同學有提到現在這版本的 `do_sift_up` 是有問題的,會在父節點都比子節點還小時結束執行,導致不會繼續檢查上層的節點。
所以要修改上述的 `while` 迴圈程式碼:
```c
static void do_sift_up(unsigned long idx,
unsigned long nr_items,
struct heap_item *h)
{
assert(idx > 0);
unsigned long pidx = parent(idx);
while(pidx>=1)
{
if(h[pidx].key > h[idx].key){
struct heap_item tmp = h[pidx];
h[pidx] = h[idx];
h[idx] = tmp;
}
idx = pidx;
pidx = parent(idx);
}
}
```
改成只要有找到父節點就會繼續執行,如果父節點小於子節點(符合 min-heap)那就會不執行交換,繼續往上找。
但是上述所說的情況只會發生在初始化這個 heap 的時候,如果是正常的 min-heap,原本插入前的 heap 就會是符合 min-heap 的節點了,就只需要判斷父節點跟子節點的大小跟交換後的關係。
如果要減少遞回或是迭代的次數,可以將初始化跟平常所呼叫的 `sift_up` 區分,讓平常所呼叫的不用繼續執行下去。