contributed by < BennyWang1007
>
AAAA = &l->head
BBBB = before
CCCC = &(*p)->next
DDDD = item->next
EEEE = (*pred_ptr)->r
FFFF = &(*pred_ptr)->r
上方程式碼首先創建indirect pointer p
指向 head 的地址(可以想像成 一個新的節點node的next = &head),接著走訪串列直到 p 指向的位置為要插入的目標節點before,此時將*p設為要插入的節點,並將節點指向的下一節點設為目標節點。
解釋:
首先我先使用快慢指標找出 list l
的中點,將 l
分成left
、right
兩半。接著分別對兩半呼叫 list_merge_sort
來遞迴地分割。分割完後,將left
、right
兩半頭部的值一一比較,並寫回 l
中,最後將剩餘的部份接到 l
的尾部,完成合併排序。
測試程式
EEEE = (*pred_ptr)->r
FFFF = &(*pred_ptr)->r
解釋:remove_free_tree
會將 node target
從二元搜尋樹(BST)中移除。首先先呼叫 find_free_tree
來獲得指向 target
的 pointer,此時分為三種情況:
NULL
完成移除。最後將 Target 的兩個child 指標皆設為 NULL,防止 dangling pointers 出現。
完整測試程式(不包括remove_free_tree
),依序將結點2, 3, 1, 4, 0 進行移除。
輸出符合預期也沒有觸發 assert。
GGGG = head->prev=prev
HHHH = list_entry(pivot, node_t,list)->value
IIII = list_entry(n,node_t,list)->value
JJJJ = pivot
KKKK = right
解釋上述程式碼運作原理
此程式碼使用迭代式(iterative)的方法實作quick_sort。
begin[]
用來儲存分割的頭和尾,因此大小為 2*list_length。rebuild_list_link(list)
將排好的子序列們連接起來,滿足雙向鏈結串列的特性。研讀〈A comparative study of linked list sorting algorithms〉,分析針對雙向鏈結串列的排序演算法考量,並運用 Linux 核心風格的 List API 予以實作。
AAAA = list_first_entry
BBBB = list_del
CCCC = list_move_tail
DDDD = list_add
EEEE = list_splice
FFFF = list_splice_tail
運作原理:
此程式碼使用 quick_sort 來對雙向鏈結串列進行排序,相較於 quiz01_3,此實作採用遞迴呼叫的方式。在每次呼叫時,創建兩個 list_head list_less
, list_greater
用來儲存兩個分割後的雙向鏈結串列。與一般 quick_sort 分割方式一致,使用第一個元素當成 pivot
並從原本串列移除,接著走訪整個串列,並一一將節點放入兩個串列,最後遞迴地對兩個分割後的串列進行 quick_sort。合併的部分,首先將 pivot
放到雙向鏈結串列的頭,接著將 list_less
拼接到串列的頭,最後將 list_greater
拼接至尾部,最終串列會是 head -> list_less -> pivot -> list_greater -> head 的順序。
改進 random_shuffle_array:
首先證明此隨機法的機率。
證明:定義 為第i個節點在 shuffle 後,成為第 j 個節點的機率。第 i 個節點在第 i+1 次交換時,有均勻的機率 此後每次交換皆有 的機率(假設第 k 次交換)與 k 交換,因此最終的分布為:
發現可合併 k 的兩種情況,以及
此隨機法是隨機的。
但在實際測試時發現結果並不是隨機的,如下圖所示:
往下深究發現其實 get_unsigned16() 本身給的結果並不完全隨機,如下圖,橫軸為 [0, UINT16_MAX],總共隨機 100000000 次,其中非零的點只有 569 個,比 10 大的點更只有 373 個。
修改:使用內建的 rand() 函數即可解決問題。
GGGG = 14
HHHH = 2
IIII = 0
JJJJ = 3
KKKK = 2
LLLL = 1
MMMM = ~1
NNNN = 1
PPPP = 2
解釋:此函數中 c
表示的是遞迴的深度,初始為 0
,每次呼叫函式時首先將數字 x 分成 upper
和 lower
兩部分,當 c = 0, 1, 2, 3 時 lower
為右方16, 8, 4, 2 bits,因此 GGGG
為 。接著判斷 c 是否為 3(最高的深度),此時返回 magic[upper]
或 2 + magic[lower]
(此處因為是2 bits 2 bits 判斷,因此 +2)。而 magic
表示的是在該 2 bits 中最左方有多少個 0,因此 magic[] = {2, 1, 0, 0};
。
因為是向上取整,在函數結尾加上判斷是否為二的冪即可,如下程式碼。
一旦發現效能改進的機會,可準備提交改進 int_sqrt 程式碼到 Linux 核心
AAAA = map->bits
BBBB = map->bits
CCCC = first->pprev
DDDD = n->pprev
EEEE = n->pprev
針對 Linux 核心的 hash table 實作,提供更多圖例並揣摩 Linux 核心開發者
map_add
中,首先使用 find_key
嘗試獲取 key 是否存在在 hash map map
中,若存在則返回。不存在時,先創建一個全新的 hash_key 並將其節點 n
插到 bucket 的頭 h
(next
指向 h
中的第一個節點,若第一個節點不為空,則第一個節點的前一個節點指向節點 n->next
的地址,並將 n
設為 h
的第一個節點,以及n的前一節點指向 h->first
的地址來完成插入)。注意: Linux 核心內部大量使用 hash table,但並非每處的雜湊函數都滿足減少碰撞及避免 clustering 現象的原則,也就是存在若干改進空間
註:vol 3 Section 6.4 閱讀需要付費,無法完成。
fncache
來儲存是否該檔案能被存取,其中 shash()
為該實作的雜湊函式。pnsocks.hlist
用來儲存、管理與查找 Linux 核心中的 Phonet sockets。其中在 pn_hash_list()
中,使用 Phonet socker object id obj
的索引來計算雜湊值。