contributed by < TSAICHUNGHAN >
1
: quicksort參考 : vax-r
鏈結串列結構體:
list_add()
使用 list_add
將 node_t
加入到鏈結串列開頭
list_tail()
從 *left
開始訪問每個節點,直到訪問到尾部 (*left)->next = NULL
就將其回傳。
list_length()
方法類似 list_tail()
,只是在每次走訪節點時會記錄一次,最終回傳走過的節點數。
list_construct()
創建一個 node
節點並分配記憶體空間,再將其節點插入鏈結串列的開頭。
list_free()
逐一走訪每個節點,並釋放記憶體空間直到尾巴(即 *list = NULL
)
quick_sort()
定義 L
、R
, pivot
指向 L
, p
指向 pivot
的下個節點,並將 pivot
斷開,後續會將其歸類在單獨 begin[i+1]
中。
*p
由左至右訪問每個節點,若節點的 value
小於 pivot
將其放至於 *left
,若大於 pivot
將其放至於 *right
,分類完成後如下圖所示 :
此時 pivot
換成 begin[2]
所指向鏈結串列的第一個,重複一樣的分類方式。
由於 L
與 R
都只剩一個節點,因此 L == R
的結果會依序將 begin[3]
、 begin[2]
、 begin[1]
送入 result
中,如下圖 :
此時經過一連串 i--
回到了 begin[0]
,重複以上動作如圖所示 :
最終結果 :
引入 Linux Kernel API
引入 list.h
將 node_t
結構體改寫成 :
改寫 quicksort
:
完整程式碼請參考:quiz1+2
2
: Timsort特性:
merge()
逐一比較兩個已排序的鏈結串列 a
跟 b
的頭節點,若較小的值則放入 head
的鏈結串列的尾部,其中 tail
指向鏈結串列的尾部,比較直到一方指向 NULL
,則另一方接續 tail
。
build_prev_link()
建立 prev
連接上個節點,使鏈結串列形成環狀雙向鏈結串列。
merge_final()
與 merge()
相似,但多了 prev
的連結,及 build_prev_link()
,來形成環狀雙向鏈結串列。
find_run()
在 Timsort 中用於找到已經排序好的鏈結串列並計算子序列大小 len
,若為降序則將其反轉。
merge_at()
在特定位置 at
連接兩個已排序的 run 並更新 run 大小,及減少堆疊的大小 stk_size
。
merge_force_collapse(),*merge_collapse()
當 run 的數量超過一定值時將進行合併,用於提升合併的效率。
來源:測驗 2
Timsort 採用一組堆疊 (stack) 來暫存 run,此舉是為了避免在一開始就掃描整個資料範圍來產生 run,從而降低記憶體開銷。過程中,Timsort 運用 merge_collapse
函式來確保堆疊上的 run 長度保持平衡。該函式主要檢查堆疊頂端的 3 個 run 是否滿足以下原則:
當不符合這些條件時,函式會決定是否進行合併。例如,考慮 3 段 run: A 的長度為 30,B 的長度為 20,C 的長度為 10,由於 A 的長度不大於 B 加 C 的總和(亦即 ),且 C 的長度小於 A,因此會選擇將 C 與 B 進行合併,形成兩個新的 run
Timesort()
使用 find_run()
函數來尋找 run ,找到 run 之後將其添加到 stack 中,同時使用 merge_collapse()
與 merge_force_collapse()
進行合併,最後使用 build_prev_link()
將其形成環狀鏈結串列。
1
: Binary TreeLinux Kernel 裡 hash table 結構體:
代表 hlist_head
的頭節點
Linux 核心的 hash table 實作中,用以處理 hash 數值碰撞的 hlist_node
:
INIT_HLIST_HEAD()
初始化 hlist_head
的 first
使其指向 NULL
hlist_add_head()
在 hlist
鏈結串列的頭插入一個新節點
參考 hlist 数据结构图示说明裡面有更完整的圖示表達插入頭節點的過程。
其中說明 pprev
為何要宣告為指標的指標的目的。
新節點的 pprev
指向指著自身節點的指標,因此將 h->first
指向新節點時新節點的 pprev
也會順道更新。
圖解:
find()
在雜湊表中查找指定值的位置索引 idx,如果找到則返回該值在列表中的索引,否則返回 -1。
其中 int hash = (num < 0 ? -num : num) % size;
計算指定數值 num 的哈希值,並將其與哈希列表的大小取餘數,以確保得到的哈希值在合法範圍內。
hlist_for_each (p, &heads[hash])
訪問雜湊表中特定的 bucket
若找到對應 num 則回傳對應的位置索引,若無回傳 -1 。
Hash Table 相關資料 :
資料結構學習筆記:雜湊表(Hash Table)
Linux 核心的 hash table 實作
struct TreeNode *dfs()
由 inorder
/ preorder
重建二元樹。
由前序( preorder )可知哪個節點在上面 (越前面的越上面)、而由中序( inorder )可知哪個節點靠左(越前面的越左邊),於是可得知前序的第一個元素一定是根,且該元素對應到中序後,左邊的值就在左邊,右邊的值就在右邊,在從右邊的值對應 preorder 即可找出頂點,以此類推可以建構出樹。
遞迴的中止條件是在 inorder 中找不到對應元素。
node_add()
將新的節點添加到哈希列表中,以便後續可以根據特定的值快速查找到該節點。
struct TreeNode *buildTree()
先將 inoreder 節點建立 hash table ,再傳回 dfs()
函式 來建樹。
延伸問題:
pre-order walk
會找到這段:在了解這段程式碼之前先來了解什麼是 cgroups
,其全名為 control groups ,他是 Linux Kernel 中的功能,可以用來限制,控制與隔離 Process 所使用到的系統資源,例如 CPU, Memory, Disk I/O, Network…等。
參考來源:第一千零一篇的 cgroups 介紹
以 root
自身當成第一個走訪的節點next = css_next_child(NULL, pos)
,若子節點存在走訪相鄰子節點 next = css_next_child(pos, pos->parent)
,若子節點不存在則尋先前的 root 走訪 pos = pos->parent
。
2
: LRU CacheLeetcode : 146. LRU Cache
LRU 說明:Least recently used (LRU)
由於 hlist
大致邏輯相同於lab0-c 的 list.h
,因此直接探討 LRU Cache 的部份
LRU 結構體
dhead
: 雙向鏈結串列的頭節點
hhead[]
: 一個 hash
頭節點的數
node
: 用於將緩存項目連接到 hash
link
: 用於將此緩存項目連接到雙向鏈結的結構
lRUCacheCreate()
開頭 int capacity
表 Cache 的最大容量
INIT_LIST_HEAD(&cache->dhead)
初始化雙向鏈結串列
INIT_HLIST_HEAD(&cache->hhead[i])
這個循環初始化了哈希表的各個 bucket 的頭部,使其成為空的哈希列表
lRUCacheFree()
用 LRUNode *cache = list_entry(pos, LRUNode, link)
獲取該LRU節點的結構,並做釋放的動作。
FFFF
為 link
,GGGG
為 &cache->link
lRUCacheGet()
與測驗1
的 find()
函式相似, hash 透過 hash = key % obj->capacity
取得,在用 hlist_for_each()
走訪 hash 值對應的 hhead[hash]
鏈結串列,若有找到對應的 key 值將及節點放到雙向鏈結串列的頭並回傳該節點的 value
。
HHHH
為 node
, IIII
為 &cache->link
lRUCachePut()
在訪問每個節點的過程若找到對應的 key 使用 list_move(KKKK, &obj->dhead)
將該節點移動至雙向鏈結串列的頭,即最近使用過。
若 cache = NULL
表示 Cache Miss ,然後有兩種補同情況的處理方式:
dhead
的最尾端節點刪除 ,並將節點插到 hhead
的頭部hhead
的頭部,同時添加到 dhead
的前面JJJJ
為 node
, KKKK
為 &c->link
3
: find_nth_bit參考: yu-hsiennn 的測驗3
BITMAP_LAST_WORD_MASK(nbits)
: 利用 mask 將不用的位元過濾,因電腦的未有可能為 32-bit 或 64-bit
__const_hweight64(w)
:計算 Hamming 權重,及二進位有幾個1
__ffs
AAAA
為 0xffffffff
舉例說明:
__clear_bit
巨集 BIT_MASK(nr)
為只有第 nr 個 bit 為 1 的二進位值
巨集 BIT_WORD(nr)
位元位置在 BITS_PER_LONG
之內
若要指定清除 *p
指定的第 nr
個 bit 只須將 mask
取反,及 ~mask
BBBB
為 ~mask
fns
基於 __ffs
可以求出第 n 個被 set 過的 bit 的所在位置。
舉例說明:
延伸問題: