contributed by <As7r1d
>
INIT_LIST_HEAD
達成該函式要求,並且使用 malloc
配置記憶體區塊給 head。element_t
,element_t
表示鏈結串列中之元素。element_t
所佔據的空間。strdup()
為一個字串分配新的記憶體,並把原始字串的內容複製過去。這樣就不會跟原始字串共用同一塊記憶體。malloc(sizeof(element_t))
動態配置記憶體空間給新節點。strdup(s)
為 value 配置記憶體並存入字串內容,複製字串 s 並存入 value。list_add(&new_element->list, head)
把新節點加到佇列開頭。list_add_tail(&new_element->list, head)
把新節點加到佇列尾端。功能:
實作內容:
list_entry(node, type, member)
從 struct list_head *
回推到 type *(element_t *)
。sp
不是 NULL,就複製字串內容sp
,但最多只會複製 bufsize - 1
個字元\0
,確保字串結束。程式碼: Commit 5eb769f
qtest:
current
代表目前正在檢查的節點。從佇列的第一個有效節點開始,不包含 head。一一比對 current 和後面的所有節點,找出重複的字串。
strcmp()
比對兩個字串,如果相等,表示找到重複的節點
如果 found_duplicate 為 true,代表 current 這個節點也有重複,直接刪掉。如果沒有重複,current 就繼續往下一個節點移動。
由於會出現上述error,目前還在修正。
功能: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點。
實作內容:
temp = current->next
:暫存 current->next,確保反轉後能找到下一個節點。
current->next = current->prev
:讓 next 指向原本的 prev。
current->prev = temp
:讓 prev 指向原本的 next
程式碼:Commit a745816
qtest:
list_cut_position()
這個函式,它能夠直接將前 k 個節點切割出來形成一個新的鏈結。當 k 個節點被切割出來後,接下來反轉。這裡直接利用了 q_reverse(),因為它已經能夠有效地反轉整個雙向鏈結串列,所以不需要再手寫反轉邏輯,這樣可以讓程式碼更模組化,提升可讀性。list_splice_tail()
來將反轉後的區塊加到一個新的 result 鏈結。功能: 以遞增順序來排序鏈結串列的節點。
實作內容:
list_cut_position(&left, head, slow)
將 head 後半段的節點分割成 left,這樣 head 和 left 各自代表原來的前半段和後半段。list_del(chosen)
從原來的鏈結中移除這個節點,list_add_tail(chosen, &result)
將選出的節點加入 result。重複這個過程,直到 left 或 head 其中一個先變空。程式碼:Commit 4d3fa7e
執行 make test 時發現排序是unstable:
因此修改如下,使排序成為stable:
功能:
實作內容:
這兩個函式的目標類似,一開始的思路是左到右遍歷並檢查右側的節點,但當節點過多時對於較大的鏈結串列會很慢。因此參考 LeetCode 2487. Remove Nodes From Linked List 。直接從左到右遍歷並檢查右側的節點,如果我們先反轉鏈結串列,從右側開始往左掃描,那麼右側的節點已經是處理過的,這樣我們只需要單向遍歷一次,時間複雜度變成 O(n)。
反轉鏈結串列:
這樣我們從左到右遍歷時,其實是從原本的最右側往左掃描。
遍歷 cur,確保 cur 是目前掃描到的最大(最小)值。next_node 負責檢查 cur 之後的節點。針對 q_ascend,如果 next_node 有比 cur 小 的值,就刪除 next_node;針對 q_descend,如果 next_node 有比 cur 大的值,就刪除 next_node。最後透過 list_del() 安全移除 next_node,並釋放記憶體。
**功能:**合併所有已排序的佇列,並合而為一個新的已排序佇列
實作內容:
當在構思 q_merge 這個函式時,要將多個已排序的鏈結串列合併為一個,並且根據 descend 參數來選擇升序或降序排序。不需要頻繁地比較和插入,而是透過 list_splice_tail() 一次性合併所有鏈結串列,再進行一次排序。
取出 head 內的第一個 queue_contex_t:
queue_contex_t 是一個封裝鏈結串列的結構,其中 q 存放具體的鏈結串列。first_ctx->q 會作為合併後的最終結果。
遍歷 head 內的所有 queue_contex_t,將所有鏈結串列合併:
遍歷 head 內的所有 queue_contex_t,取得 current_queue(當前佇列)。如果 current_queue 不是空的,將 current_queue 透過 list_splice_tail() 併入 result_queue,然後更新 first_ctx->size,因為 result_queue 現在包含了更多節點。清空 current_queue,避免重複計算。繼續遍歷 head 內的下一個 queue_contex_t。
最後對 result_queue 進行排序
最終合併後的大小
程式碼:Commit 4d3fa7e
qtest: