contributed by < hankluo6
>
1
AAA
n->next = first
BBB
n->pprev = &h->first
map_add
將新的節點加入到 hash 中,find_key
先從 hash 檢查該 key 是否已經存在,如果已經存在則直接回傳,尚未存在則分配新的記憶體放置 data
。故可知 AAA
及 BBB
為將新的節點連接到 hash 上的 linked list,且為插入到 linked list 的前端。
map_deinit
釋放所有 hash 的記憶體,for
迴圈遍歷所有 hash entry,內部在一個一個把 linked list 上的節點及其 data 釋放。在遍歷每個節點時,同時透過指標的指標更改 head->firt
及 next->prev
。
n
為 data1
。pprev
的指標內指向的值(map.ht[0].first
)設為 next
;next
的 prev
設為 pprev
指標的指標,並把 n
的 prev
及 next
設為 NULL
,最後釋放。hashtable
DEFINE_HASHTABLE
& DECLARE_HASHTABLE
以 hlist_head
當作每個 hash table 上每個 bucket 的 list,其中不用 list_node
的原因為不需要用到 circular linked list。bits
決定需要 hash table 的大小。
特別要注意的為初始化 name
時使用的 ...
為 GCC extension 的 Designated Initializers,可以一次初始化所有 index 為 HLIST_HEAD_INIT
。
HLIST_HEAD_INIT
定義在 list.h
內,及初始化 first
為 NULL
。
hash_init
專門將 DECLARE_HASTABLE
宣告的 hash table 初始化,作用與 DEFINE_HASTABLE
相同。
hash_add
& hash_del
add
與 del
都為呼叫 hlist
API,其實作皆與 list_head
相似。
hash_for_each
遍歷 hash table 所有的 entry,bkt
表示 bucket,每一次迴圈透過 hlist_for_each_entry
遍歷這個 bucket 內的 linked list。
註解中的 phi = (sqrt(5)-1)/2
指的是 ,為 golden ratio 的倒數。
任意數乘上這兩個數再取其高位元可以呈現好的數值分布。在連續的數值中,利用這種 Fibonacci Hashing 方法可以將 hash 後對應的值分散到其他 hash value 的值之間 (將最大區間以 golden ratio 分割的點)。
而現實生活上的 keys 值分布常常會有某種數值分布,類似 ,例如字串集合 ,這種情況利用此種 hash function 的行為就如同計算 ,便能將 key 值映射到沒有使用過的 value,減少 collision 的機率。
假設 為一有理數,要將 的小數部分放到 之間,則會發現 放置的位置會在由前面的點分割成的線段中最長的線段,而當 過大或過小時 (接近 0 或 1),產生的分布區間便會由小而大,並不是均勻分布。
可證明能產生較好的分布的數值為 及 (Knuth vol 3, section 6.4, exercise 9.),故選擇這兩個數計算 hash。
2
COND1
head->next && head->val == head->next->val
COND2
head->next && head->val == head->next->val
根據註解可知道第二個 if
用來刪除重複的節點,故 COND1
應為判斷是否有重複的值。在 if
block 裡面最後回傳 head->next
,可知道中間 while
迴圈需跳過相同值的節點,最後停在所有有著相同值的節點中的最後一個節點,得出 COND2
。
3
MM1
list_for_each_entry_safe
MM2
list_for_each_entry
MM3
list_for_each_entry
MM4
list_last_entry
LRU 由兩個部分組成
dhead
:為 doubly-linked list,用來取得 Least Recently Used 的 node,靠近頭端部分表示存取時間與當前時間越近,靠近尾端則越遠。hheads
:為 hash map,用來儲存 key 及 value 的資訊,以 key % capacity
當作 hash function。GET
操作時,從對應的 hash 值的 list 中尋找,如果有找到,透過 list_move
將該 node 移到 dhead
的頭端,表示最近使用。故 MM2
為 list_for_each_entry
遍歷對應 hash 上的 list。
PUT
操作時,先檢查該 key 是否有存在 hash map 當中,故 MM3
與 MM2
相同,如果不存在,則要新建一個新的 node,此時若 LRU 已滿,必須剔除掉最久沒使用的節點,該節點及為 dhead
的 tail,故 MM4
為 list_last_entry
。建立完新的節點後,再放到 dhead
及對應的 hhead
頭端。
CREATE
操作時,必須將所有 list_head
透過 INIT_LIST_HEAD
初始化,把相對應的 next
與 prev
欄位設定,list 相關巨集才能正確使用。
FREE
操作時,因為每個存在於 hash map 的 node 都與 dhead
鏈結,所以只要遍歷 dhead
釋放所有節點即可。
item:dlink
與 item:hlink
為同一個物件中的不同成員
4
LLL
--left
RRR
++right
longestConsecutive
內的第二個 for
迴圈將 nums
陣列中出現的所有數字以 hash map 存起。第三個 for
迴圈遍歷整個陣列,每次迴圈有個 current value 當作基準點,left
表示往小於 current value 的方向從 hash map 中尋找;right
表示往大於 current value 的方向尋找。因為要尋找連續的序列,故每次都以加一或減一的值尋找,得出 --left
及 ++right
。每找到一個數字與 current value 為連續序列,則將其從 hash map 中移除,下次迴圈選到相同序列內的數就不需要重新計算。