contributed by < Chao-Shun-Cheng
>
q_new
: 建立新的「空」佇列q_free
: 釋放佇列所佔用的記憶體q_insert_head
: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則)q_insert_tail
: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則)q_remove_head
: 在佇列開頭 (head) 移去 (remove) 給定的節點q_release_element
: 釋放特定節點的記憶體空間q_size
: 計算目前佇列的節點總量q_delete_mid
: 移走佇列中間的節點,詳見 LeetCode 2095. Delete the Middle Node of a Linked Listq_delete_dup
: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 LeetCode 82. Remove Duplicates from Sorted List IIq_swap
: 交換佇列中鄰近的節點,詳見 LeetCode 24. Swap Nodes in Pairsq_reverse
: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點q_sort
: 以遞增順序來排序鏈結串列的節點queue.c
實作queue
資料結構從 queue.h
當中的 element_t
可以知道 Node 的資料結構包含一個 value
與 list
。其中 value
會指向一個字串,另外 list
則是會定義在 list.h
裡的一種資料結構。可以看到 list
有兩個指標分別指向下一個與上一個 Node 的 list
。
完整的 queue
資料結構如下圖所示。
q_new
new
需要先透過 malloc
進行記憶體配置,再透過 list.h
所提供的 INIT_LIST_HEAD
去進行初始化。
q_free
首先要判斷 queue
裡面是否存在節點,可以利用 list.h
提供的 list_empty
去判斷。如果有 Node 再利用 list_first_entry
去得到指向包含這個 list
的 element
。最後再利用 q_release_element
去釋放 Node 的記憶體。
其中 list_first_entry
是利用 container_of
去獲得 Node 的記憶體位置,詳細使用方法與原理可以參考 Linux 核心原始程式碼巨集: container_of。
q_insert_head
首先要先利用 malloc
去分配 Node 所需要的記憶體空間,再去分配 value
所需要的空間,最後再利用 list.h
所提供的 list_add
將 Node 插入 queue
當中。
q_insert_tail
與 q_insert_head
方法雷同,不同的是利用 list_add_tail
去插入佇列中。
q_remove_head
首先透過 list_first_entry
去獲得指向第一個 Node 的指標,再透過 list_del
去將 Node 移出 queue
。需要特別注意的是 value
與 bufsize
的大小,以及結尾字元 \0
。
q_remove_tail
與 q_remove_head
雷同,不過是透過 list_last_entry
去獲得指向最後一個 Node 的指標。
q_size
這邊是直接用 list_for_each
走訪全部節點以計算數量。還在思考如何降低計算時間。
q_delete_mid
宣告兩個指標 next_node
與 prev_node
用 while
分別從 queue
的前與後去走,當 prev_node == next_node
或 next_node->next == prev_node
就代表找到中間的 Node 了。
q_delete_dup
這題主要是參考 LeetCode 82. Remove Duplicates from Sorted List II 這題的內容來實作,不過在原題目中是有重複的要全部 delete ,因此我透過 list_for_each_safe
去看每個 Node 的值,並將值用 value
指標指著,再透過strcmp(value, target->value) == 0
來去判斷鄰近的 Value
是不是一樣,是的話就 delete
,直到遇到不一樣為止。
value
直接可以指到下一個值
delete
後面重複出現的之後,還要再刪掉自己本身
Version one
考慮刪掉自己本身的 Node ,但發現 trace-06
一直過不了。
Version two
於是嘗試不刪掉自己本身,指刪掉後面一樣的 Node ,竟然神奇的過了。不確定是不是我對題目有誤解還是有甚麼 Bug 。
q_swap
swap
這邊是利用 from
與 to
兩個指標分別去紀錄要 swap
兩個 Node 的前後關係,再透過 while
去把所有需要 swap
的 Node 進行相關的指標替換即可。
q_reverse
這邊的想法是直接將 prev
與 next
兩個指標的內容交換即可。因此直接透過 list_for_each_safe
去把每個 Node 裡的 prev
與 next
內容交換。
q_sort
這邊是利用 mergesort 下去進行排序,並透過 LIST_HEAD
將變數放在 stack
裡,就不用特別去思考如何釋放記憶體的議題。雖然測資可以通過,但當我要 git commit
時發現,queue.h
不能修改。會去研讀 list_sort.c 並進一步思考如何修改。
後來發現將 Function
放在 q_sort
上面就可以直接使用,不用特別在宣告在 queue.h
裡面,總算可以順利 git commit 了。
mergesort 主要有兩個重點,分別是將 list
切開以及合併,合併這邊參考 LeetCode 21. Merge Two Sorted Lists 的思考下去實作。
切分的部分則是利用區域變數的特性,離開函式會自動釋放記憶體的優勢,來去進行實作。
make test
自動評分花了一些時間與參考許多同學的實作方式,總算是看到皮卡丘了 QAQ
文字訊息不要用螢幕截圖展現,你的讀者可能是視障朋友,而且圖片無助於資料檢索。
已經修正,感謝老師提醒