contributed by < otischung >
補完之程式碼如下所示
由於 l->head
與 list_item_t *before
都是 struct list_item *
,因此我們只需要紀錄這些指標的位址,透過 dereference 即可操作該記憶體內容。
因此,我們的操作步驟如下:
l->head
所紀錄,其型態是 list_item_t *
,故其位址為 &l->head
,其型態是 list_item_t **
。*p
是否等於目標 before
,若相同則下一步,若不同則將 *p
的下一個 (*p)->next
的位址紀錄起來 &(*p)->next
。*p
所指之處放入 item
,並將 item->next
記成 before
。我們分析以下三種情況
p = &l->head
,因此,執行插入的時候,*p = l->head = item
,再把 item->next
紀錄成 before
,也就是之前的頭。before = NULL
,所以最後 *p = NULL
,**p
指向最後一個元素的位址,因此將最後一個元素的 next
,也就是 *p
,指向 item
,並將 item
的下一個指向 NULL
,而此時 before = NULL
,因此不違反原先的設定。由此證明,以上程式碼具有一般性,不須做其他額外處理。
補完之程式碼如下所示
要找到最右邊的 node,就從左邊一路掃到右邊就可以了。
補完之程式碼如下所示
首先,rebuild_list_link()
只在 quick_sort()
最後一行執行,而觀察 quick_sort()
只有處理 next
,並未處理 prev
,因此判定此 rebuild_list_link()
是為了補上 prev
。
因此,我們定義 prev
為第一個元素,node
為第二個元素,透過雙向環狀 linked-list 依序處理,即可推斷 GGGG 為何。
HHHH 與 IIII 皆是利用 container_of
macro 取得 node_t
的位址,再取得該 node 的值。
JJJJ 與 KKKK 皆為圖例所示,依序為 left
, pivot
, 與 right
。
補完之程式碼如下所示
其實對於 AAAA 來說,選擇 pivot 可以是任意的,不過在選項中,可以使用 container_of
得出「一個」 node 的 macro,只有 list_first_entry
,那麼他就是唯一解。
與測驗 1-3 不同的是,這裡使用遞迴的方式實作,額外準備兩個 list 做遞迴。
取出 pivot 後,將 pivot 移出 list,之後將每個 node 與 pivot 作比較,分別移入兩個 lists。
至此,原本的 head 已經沒有東西了。
遞迴解完之後,先把 pivot 放回 head,再把左邊 list 插入頭段,最後把右側 list 插入尾段。
補完之程式碼如下所示
這個實作是使用遞迴呼叫求解 counting leading zero,直到剩下 2 bits 為止。
每次都將目前的位元平分成 upper 與 lower
c | upper/lower 位元數 |
---|---|
0 | 16 |
1 | 8 |
2 | 4 |
3 | 2 |
當 c == 3
時,則 upper 與 lower 只剩下 0b00
, 0b01
, 0b10
, 0b11
三種可能,而這些數字的開頭分別有 {2, 1, 0, 0}
個 0,因此,當 c == 3
時,
接下來,我們補完開平方根的程式碼
我們的目標是要找到一個最大的數字 使得 。
我們知道對於所有正整數與 0 可以以二進位表示,即
對於所有 為正整數或 0, 為 0 或 1。
因此,對於所有 ,找到最大的 使得 ,由此開始進行二分搜尋法,繼續尋找 , , …, , ,即可得到
由於我們總是從 開始搜尋,因此 shift
必定是偶數,而又因為我們的起始項不超過 ,因此我們總是向下取偶數,這等同於將 LSB mask 成 0,即 & ~1
。
接下來,我們看一下 的例子,170 在二進位表示成 0xAA
,因此 shift == 6
,此時我們有 , , ,
由於 ,所以 。
接下來,我們搜尋 是否正確
我們將等式做展開