contributed by < Chao-Shun-Cheng
>
1
map_init
map_init
會建立 個大小的雜湊表,如下圖所示。其中 ht
會指向 2^n * sizeof(struct hlist_head)
的連續記憶體空間。
首先第七行用 malloc
建立 hashtable
所需要的連續記憶體空間,空間大小為 個 sizeof(struct hlist_head)
再來就是進行初始化的部分,使得 hashtalbe
都沒指向任何內容。
find_key
find_key
有兩個輸入參數,分別是 map
跟 key
,會從 map
去尋找有沒有符合的 key
,有找到就會回傳對應的 hash_key
,否則就回傳 NULL
。
首先可以看到第二行,hash
是雜湊函式,會利用 key
去找到 hash table
中對應的 hlist_head
不同的 key
經過雜湊函式計算後可能得到同樣的結果,所以會在用一個 for
迴圈去看每個結果的 key
是不是跟 input
的 key
一樣,一樣的話就代表有找到,會回傳找到的 hash_key
。第三到第七行就是在進行這個功能。
其中 container_of
可以從一個結構體中的每個成員,去得到結構體的最前面的地址,詳細可以參考Linux 核心原始程式碼巨集: container_of
map_get
map_get
搭配 find_key
去使用,目的是用來查看是否已經存在。
map_add
map_add
會將資料存進 hash_table
當中。
首先看到第三行,如果 key
已經存在 hash_table
當中,就不用再加進去 hash_table
。
如果找不到則會利用 malloc
分配一個 hash_key
的空間,並且初始化。
接著利用雜湊函式 hash
找出要放置在 hash_table
的位置,並用 h
指著 hlist_head
。另外 n
指向剛剛建立的 hash_key
當中的 node
、first
則指向 h
當中的第一個 node
。
緊接著要將剛剛建立的 node
插入進 list
的頭,以下是對應的操作。n->next
要指向原本的 first
,所以 AAA
是 n->next = first
;再來要去要去看原本到底有沒有 first
,如果本來有,則要調整原本 first
的 pprev
(13、14 行);最後要將插入的 node
的 pprev
指回去 list
,所以 BBB
是 n->pprev = &h->first
。
twoSum
twoSum
利用上面所建立的 hash table
的操作來進行實作。
首先利用 map_init
初始化 map
所需要的空間 (第 3 行),以及用 malloc
建立回傳答案所需要的空間 (第 5 行)。
接下來是利用一個 for
迴圈去建立 hash_table
與查找 key
。如果 key
可以從 hash table
中找到就回傳答案,如果找不到,就將 key
加進去 hash_table
當中。
hash table
如何使用在 twoSum
可以參考 2022q1 第 1 週測驗題之說明。
2
deleteDuplicates
deleteDuplicates
要可以將所以重複出現的 Node
都 delete
掉,以下是實作方式。
首先可以注意到我們需要去比較下一個 node
與現在的 node
是不是一樣的值,所以要先確定有下一個 node
,再去看下一個 node
的值,因此 COND1
就會是 head->next && head->val == head->next->val
;緊接著可以看到有提示 /* Remove all duplicate numbers */
,因此可以知道 while
迴圈會去把相同的 node
都 delete
掉。因此 while
的條件就會是要有下一個 node
並且值是一樣的,因此 COND2
就是 head->next && head->val == head->next->val
。
3
lRUCacheCreate
lRUCacheCreate
會依據 capacity
的大小,建立出相對應的空間,並且將 dhead
與 hheads
進行初始化。
其中在第三行的 sizeof(*obj)
其實與 sizeof(LRUCache)
是一樣的。
lRUCacheFree
lRUCacheFree
會將 obj->dhead
之所有 Node
給釋放掉。因此 MM1
會是 list_for_each_entry_safe
用來去歷遍所有的 Node entry
。
lRUCacheGet
lRUCacheGet
會利用 key
去看目前的 hheads
當中有無一樣的 key
,如果有就代表有存在 cache
當中。因此 MM2
是要用來歷遍 bucket
的,所以會是 list_for_each_entry
。
lRUCachePut
lRUCachePut
可以分成三個部分,首先是已經存在 cache
當中,要把 node
更新到 list
最前面;再來就是如果 cache
容量已經滿了,要把最舊的 node
移除,並且放進新的 node
;最後就是還有空間,可以直接放進去新的 node
。
可以發現這邊就是要找尋是不是已經在 cache
當中,因此要歷遍 bucket
去看有沒有符合的 key
。因此 MM3
是 list_for_each_entry
。假如有找到就會進行第七行,將這個 node
移至 list
最前方。
這邊則是判斷還有沒有容量,假如容量不夠,就要移除最舊的 node
,所以 MM4
會是 list_last_entry
,用來移除 list
中最後面的 node
。
4
find
find
會去從 bucket
當中去尋找是否有對應的數字 num
。
longestConsecutive
longestConsecutive
是利用 hash table
將資料先分成不同 bucket
再找出最長的連續數字。
首先看到這個 for
迴圈,會把每個數字經過 hash fuction
計算後,放進對應的 bucket
當中。
再來透過 for
迴圈去看每個 node
。第 23 ~ 26 行會把 node
的 num
提出來並且移出 bucket
;緊接在再往 num + 1
與 num - 1
去尋找是否有對應的 node
,有的話就繼續找,直到不連續為止。因此可以得知 LLL
是 --num
而 RRR
是 ++num
。