contributed by < max890808
>
參考 Optimized QuickSort — C Implementation (Non-Recursive) 實作非遞迴 (non-recursive; iterative) 的快速排序法
begin
和 end
用來紀錄比較的範圍
left
存放比 pivot
的 value 小的 node_t
right
存放比 pivot
的 value 大的 node_t
圖解
假設有一串列 [2, 0, 4, 1, 3] ,此時 i 為0 ,left
和 right
皆為NULL
遍歷整個串列,比 pivot
的 value 小的會被放入 left
串列,反之放入 right
串列
更新 begin
和 end
進入下一次迭代,此時 i = 2
此時 begin[4]
= 4 和 end[4]
= 4, 不滿足 if 的條件,因此進行 else 的動作,透過 list_add
將 begin[4]
放入 result
,並將 i
減一
當索引 i 扣到小於 0 時,while 條件不成立跳出迴圈,完成排序,將 result 指派給 list
commit 7ff3f48
將原本的結構體改為使用 list_head
,詳細修改可以看 commit 紀錄
max_level
為 2 * list_legnth(list)
,但最差的情況下只需要 list_legnth(list) + 1
,因此作以下修正:end[i]
可由 begin[i]->prev
取得,因此不需要使用額外的儲存空間commit 4644839
最差的狀況會發生在 Pivot
恰好是該資料陣列的最小值或最大值,這裡的解決方式為 Randomized Quick Sort
,用亂數選取的方式,隨機挑一個值作為 Pivot
,並使用 <time.h>
比較最差狀況的結果
資料總數 | quick_sort 第一次 (s) |
quick_sort 第二次 (s) |
quick_sort 第三次 (s) |
quick_sort 平均 (s) |
random_quick_sort 第一次 (s) |
random_quick_sort 第二次 (s) |
random_quick_sort 第三次 (s) |
random_quick_sort 平均 (s) |
---|---|---|---|---|---|---|---|---|
1000 | 0.009 | 0.008 | 0.009 | 0.009 | 0.001 | 0.001 | 0.001 | 0.001 |
10000 | 0.600 | 0.621 | 0.578 | 0.600 | 0.005 | 0.006 | 0.006 | 0.006 |
100000 | 55.475 | 55.576 | 57.474 | 56.175 | 0.057 | 0.057 | 0.070 | 0.061 |
Timsort 定義一個 minrun
控制每個 run 的長度,使 run 的數量等於或者略小於 2 的冪,這樣可以提高合併的效率。並且採用一組堆疊 (stack) 來暫存 run,避免在一開始就掃描整個資料範圍來產生 run,從而降低記憶體開銷。此外,透過 merge_collapse
函式確保 run 的長度,函式策略如下:
當不符合上述條件時,則會判斷 A 、C 大小,小的跟 B 合併。在合併兩個 run 時,Timsort 會有一個機制決定是否要啟動 Galloping mode
。
不斷呼叫 find_run
函式找出下一個 run,並根據 run 的長度和目前的堆疊狀態,呼叫 merge_collapse()
合併不滿足條件的 run。
使用 build_prev_link()
使原本單向串列成為環狀雙向鏈結串列,並透過 marge_final()
將最後兩段 run 進行合併。
MIN_GALLOP
判斷 galloping mode
和 one-pair-at-a-time mode
的使用時機TODO :
Linux 核心的 hash table 與典型的雙向環狀鏈結串列不同,hash table 所使用的 pprev 宣告為指標的指標,這樣的好處為 hlist_head
僅需要保存一個指標,當 bucket 很大時,就能減少記憶體開銷。示意圖如下:
find
透過雜湊值和 list_entry
找出目標結構體的位置,回傳相對應 idx
值
node_add
計算雜湊值判斷要將節點加入到雜湊表的哪一位置
dfs
透過 find
函式找到目前 root 的 idx
值,分別將 preorder 和 inorder 切成 left, root 和 right 三部分,不斷的遞迴重複上述步驟直到 preorder 或 inorder 陣列長度為一
commit 781cdee
修正版
commit 30b0614
透過 generateRandomTree()
隨機生成一顆樹
preOrderTraversal()
和 inOrderTraversal()
負責找出前序和中序的陣列
之後將前序和中序傳入 buildTree()
得到所要測試的樹,再透過 preOrderTraversal()
和 inOrderTraversal()
得到所要測試的樹的前序和中序陣列,最後比較一開始的前序和中序陣列是否相等就能完成測試
TODO:
LRU(Least Recently Used Cache) 是一種快取的實做方式,概念是會儲存最近用過的內容,會透過 Hash Map 與 Double Linked List 來搭配實做,如果資料愈常被使用,資料會被擺在 List愈前方的位置,如果快取滿了,則會從 List 最末端元素開始移除。
因為 List 會從第一筆逐一查詢,所以查詢的時間複雜度為 O(N),為了降低查詢時間,所以才搭配HashMap,在 HashMap中設置 key,讓 key 的內容對應到 List 中的元素,就可以讓查詢時間降低到 O(1)
LRUCache
capacity:
紀錄 cache 能存放的最大資料數
count:
cache 當前的資料數
dhead:
資料的頭節點
hhead[]:
雜湊表
lRUCacheGet
計算 hash 值後,在對應的 hhead[hash]
所指向的鍵結串列進行搜索,若找到該節點回傳對應的值,並且透過 list_move
更新 dhead
lRUCachePut
將新的資料放入快取當中,若資料已經在快取中,則使用 list_move
更新 dhead
。若資料不在快取中且快取空間已滿,則會刪除最久沒被使用的資料,加入新的節點。若資料不在快取中且快取空間還沒滿,則配置新的記憶體空間並加入新節點。
TODO:
hweight_long
計算 w
當中有幾個位元被設定
__ffs
找 word
第一個被設定的 bit 的位置
fns
透過 __ffs
函式得到第 N 個被設定的 bit 的位置
find_nth_bit
透過 small_const_nbits
巨集判斷 size
的大小,若 size
小於 BITS_PER_LONG
,則運用 fns
函式找出第 N 個被設定的 bit 的位置,反之則會進入到 FIND_NTH_BIT
巨集
FIND_NTH_BIT
每次迭代都計算 FETCH
的 BITS_PER_LONG
長度有幾個位元被設定,透過 found
找出目標答案
TODO: