contributed by < hankTaro
>
Linux 核心設計
struct element_t
在開始前,先確認其節點的架構,以便進行撰寫
q_new
q_free
由於 list_head
嵌入於 element_t 的結構中,使其可以成為 doubly-linked list,要釋放的是結構整體,而非 list_head
本身,所以使用 q_release_element()
而非 free()
由 malloc for struct and pointer in C 所言,由於宣告 element_t
時,只有架好 1 的部分,其中 x 指標所指的空間(2)需要另外分配
使用 Graphviz 重新製圖。
:notes: jserv
已更改
同理,free(list_head *ptr)
只會將其所指的 list 節點釋放(也就是上圖中,1的部分),並未釋放內部指針指向的空間(也就是上圖中,2的部分)
最後釋放 list_head
結構的 l
q_insert_head
/ q_insert_tail
起初使用 new->value = s;
並未使用 strup(s)
複製字串並進行空間的分配,由於 element_t 中的 value 是指標,需要為其分配記憶體位置
所以使用 strdup() 來進行動態記憶體配置
The strdup() function returns a pointer to a new string which is a duplicate of the string s.
Memory for the new string is obtained with malloc(), and can be freed with free().
根據 Dangling Pointer 概念,在 free()
後將 new 指向 NULL
function 結束後會自動釋放清除
思考如何縮減重複的程式碼,從而達到更精簡的目標。
:notes: jserv
q_remove_head
/ q_remove_tail
依照 queue.h
中的 q_remove_head
說明
NOTE: "remove" is different from "delete"
If sp is non-NULL and an element is removed, copy the removed string to*sp
(up to a maximum of bufsize-1 characters, plus a null terminator.)
Return: the pointer to element, %NULL if queue is NULL or empty.
q_size
q_delete_mid
有想到兩種做法
本次使用第一種方法,想法是多加利用雙向串列的優勢,減少 次尋訪時間
q_delete_dup
*start
紀錄其起始點,用 *it
進行尋訪,並同時確認it->value
與 it->value
內容是否相同,直到抵達兩者內容不重複的 node ,開始判斷 it
與 *start
中是否有 node 存在,若存在就代表起始點的值存在重複,則使用 list_cut_position(&tmp, start, end->prev)
將其部分移出至 del
,最後統一清理掉,並將 safe->list.prev
設為新起始點
(it 所指位置有可能在list_cut_position()
中被 remove,故不使用其作為新起始點)
q_swap
q_reverse
想法是將 node 中的 next
指向 prev
並且 prev
指向 next
,並使用迭代對每個節點做轉換,此行為不難做,所以重點在於如何尋訪
q_reverseK
*start
紀錄其起始點,每尋訪 K 個 node 便將起始點到當前的點用 list_cut_position()
分割到串列外,做 reverse()
隨後再用 list_splice_init()
將其,同時更新 start
位置,做為下次的分割起點以及合併點
撰寫更精簡的程式碼。
:notes: jserv
q_sort
參考你所不知道的 C 語言: linked list 和非連續記憶體以及 seasonwang0905的 Merge sort 寫法,先寫出合併的函式
接下來寫出遞迴的分割函式,其中的struct list_head *L1 = mergesort(head);
宣告十分重要,不能不宣告寫出 return merge_two_list(mergesort(head), mergesort(fast));
,因為 mergesort(head)
的型態是 list_head
,會需要用另一個 list_head
去指向他,否則進入 merge_two_list()
後,mergesort(head)
被作為 head
無法被 container_of()
,其中的值也就無法被取出(此階段會使整體 element 減少1)
此處不可使用 list_empty(head)
因為末端不會指向 head
只會指向 NULL
此函式的想法是,先選取雙向串列中其中一向,將其處理成單向鏈結串列進行排序,再依據其順序依序走訪,將另一項的串列重新定向
疑問:
要怎麼知道傳入的head 是否有被嵌入於 sturct 中,像是q_sort中head->next = mergesort(head->next);
傳入的值是確定嵌入於element_t
中的
因為如果 head 有被嵌入於 sturct 中,那就會需要head = mergesort(head);
去求出正確的排序,否則 head 中的值不會被排序到
如果是 head 有被嵌入於 sturct 中,感覺用head = mergesort(head);
也不是不行,直接重新找出個 head 出來(已排序),反正出來一定是個排序好的單向串列,再去把prev整理一下就好了有被嵌入於 sturct 中的 list_head 可以當作head嗎?(個人猜測可以 但這邊的head應該沒有)
有規定 sturct 的 head 一定要屬於 被嵌入於 sturct 中嗎?(個人猜測沒規定 因為這邊兩者都有使用)
改進你的漢語表達,什麼是「包著」?請善用 GDB 追蹤。
:notes: jserv
q_descend
其說明
Remove every node which has a node with a strictly greater value anywhere to the right side of it.
換個說法就是從 tail 開始逆向尋訪,並先將 tail 設為 max,任何值比其小的節點,都必須被移除,直到遇見比其大的節點,將 max 更新為當前節點,直到尋訪到 head,每當有一個點成為 max
就代表此點最終會存在於串列中,所以 count++
q_merge
先將串列中的空子串列移除,必免 while (first->q && second->q)
遇上空串列而忽略另一個非空串列
e.g. lists = [[1,4,5],[1,3,4],[],[2,6]]
, 當 first==[], second==[2,6]
, [2,6] 會被忽略
由於有 個串列需要合併,會需要合併 次,當 為奇數,會需要合併 次,所以設 count = (n+1)/2
其中 merge_two_list
的功能與 q_sort
中的 merge_two_lists
相似,不過這裡的需要在函式內將雙向串列的結構整理好,所以使用 list_move_tail
來移動最小值至目標地點
由於最後排列好的串列會位在 head
指向的第一個 entry ,所以最後 return 其 size 即可