# 2025q1 Homework1 (lab0) contributed by < `hwz222` > {%hackmd NrmQUGbRQWemgwPfhzXj6g %} ## 實做佇列各函式 `TODO: 95/100 未通過 constant time 測試` ### > commit [196cf10](https://github.com/hwz222/lab0-c/commit/196cf10efc1dfd45ca30910cc3cf3ee9b323ec25) 動態配置新的 list_head 作為新佇列開頭,配置後檢查是否配置成功,並使用 `INIT_LIST_HEAD` 巨集初始化成員指標變數 `next` `prev` 指向自己位址。 ### q_free > commit [a5dabbb](https://github.com/hwz222/lab0-c/commit/a5dabbb60db4cad818ae5c86125861070c21e140) 我的實做方法是在走訪佇列同時釋放每個 `element_t` 記憶體,考慮到此函式在走訪時會對佇列結構做更改,我使用巨集 `list_for _each_safe` 走訪佇列並釋放 `element_t` 成員的記憶體空間,最後再釋放佇列頭部的 `list_head` 。 ### q_insert_head > commit [bef50fe](https://github.com/hwz222/lab0-c/commit/bef50fee531e01bc81cbe85d2fc7198db5b474f1) 先判斷佇列開頭指標與插入字串指標是否為空指標,接著動態配置 `element_t` 與其成員變數 `value` 記憶體空間, `value` 使用 `strdup()` 函式進行動態配置,確保配置成功後使用巨集 `list_add` 將節點插入至佇列頭部。 ### q_insert_tail > commit [bef50fe](https://github.com/hwz222/lab0-c/commit/bef50fee531e01bc81cbe85d2fc7198db5b474f1) 實做方式與 `q_insert_head` 相似,最後插入則是使用 `q_insert_tail` 插入節點至佇列尾部。 ### q_remove_head > commit [e3aea57](https://github.com/hwz222/lab0-c/commit/e3aea57943fd2fa6693ab5cad1c3ad7751d4dd4b) 使用巨集 `list_first_entry` 搭配 `list_del` 找出佇列第一個元素並切斷成員變數 `list` 與前後元素之連結但並不釋放空間;將移出元素之 `value` 透過 `strncpy` 淺複製至 `sp` 指標。 ### q_remove_tail > commit [e3aea57](https://github.com/hwz222/lab0-c/commit/e3aea57943fd2fa6693ab5cad1c3ad7751d4dd4b) 與 `q_remove_head` 相似,尋找佇列尾端元素搭配的巨集改為 `list_last_entry` 實做。 ### q_size > commit [f372fbf](https://github.com/hwz222/lab0-c/commit/f372fbf89b6ce44367010b80859faface57ee352) 檢查佇列頭部非空指標後使用 `list_for_each` 走訪佇列同時計算佇列大小。 ### q_delete_mid > commit [d03210a](https://github.com/hwz222/lab0-c/commit/d03210a6e5232529dd256087f54ce4716e45a473) - 使用快慢指標 `slow` `fast` 走訪佇列,考慮佇列長度為奇數時,排在第 $⌊n / 2⌋$ 個元素要執行 `delete` 操作,迴圈的結束條件設定為當 `fast` 的下下指標指向 `head` 時,`slow` 指標剛好位於中心元素。: ```c while (fast->next != head && fast->next->next != head) { slow = slow->next; fast = fast->next->next; } ``` - `delete` 刪除元素時,先使用一指標變數紀錄刪除節點位址,再使用 `list_del` 斷開中間節點於左右節點連結,再釋放該元素之成員及自身配置的記憶體空間。 ```c element_t *del_elem = list_entry(slow, element_t, list); list_del(slow); free(del_elem->value); free(del_elem); ``` ### q_delete_dup > commit [b562292](https://github.com/hwz222/lab0-c/commit/b5622922b0140fb40918f999b393b40511c4b8a7) 走訪佇列時使用 `next_lh` 紀錄下個即將走訪的元素。比對與現在走訪的元素,若相同使用與 `q_delete` 相同方式切斷與鄰元素的連接並釋放記憶體空,繼續下個節點的比對,直到沒有相同元素出現,釋放 `curr_lh` 本身所配置記憶體,繼續走訪下個相異元素。 ### q_swap > commit [0bfda59](https://github.com/hwz222/lab0-c/commit/0bfda598160e28a364302527c6e35dd244860df2) 一開始採取的實做方案為讓佇列元素兩兩一組,交換兩成員變數 `list` 內的值,即可達到順序對調的效果 ; 改善程式碼整潔度,使用巨集 `list_move` 將要交換的後節點移位到前節點之前,一樣達到交換效果。 ```diff - tmp = current->next; - list_del(cur->next); - list_add(tmp,seg_head); + list_move(curr->next, seg_head) ``` ### q_reverse > commit [29eb433](https://github.com/hwz222/lab0-c/commit/29eb4339436707f63184efcee27e898095ac3254) 一開始的實做方式是走訪佇列,並交換每個元素的 `list->prev` 及 `list->next` 可達到反轉佇列效果 ; 為改善程式碼的整潔度使用 `list_for_each_safe` 及 `list_move` 實做反轉佇列,走訪時將元素移到頭節點,走訪到尾端時等同於反轉整個佇列。 ```diff - struct list_head *ptr = head, *tmp; - do { - tmp = ptr->prev; - ptr->prev = ptr->next; - ptr->next = tmp; - ptr = ptr->prev; - } while (ptr != head); + struct list_head *safe, *ptr; + list_for_each_safe(ptr, safe, head) + list_move(ptr, head); ``` ### q_reverseK > commit [f7c6eac](https://github.com/hwz222/lab0-c/commit/f7c6eac844f0e7cee7a3d6337d4bfaf434fc7e05) 走訪佇列時每 $K$ 個元素當成一個看成一個佇列,此子佇列開頭為一個子佇列結尾,用與 `q_reverse` 相同的方式將每個元素移到開頭完成反轉子佇列。 ```c for (int i = 0, bound; i + k <= length;) { bound = i + k; while (i < bound) { tmp = curr->next; list_move(curr, seg_head); curr = tmp; i++; } seg_head = curr->prev; } ``` ### q_ascend > commit [7865873](https://github.com/hwz222/lab0-c/commit/78658738d5e96458aa670a4c0d60ac53b5541b39) 從佇列尾端開始反向走訪,維護一個 `upper_bound` 使得下個走訪對象不能 $>$ 該元素,並持續更新使佇列嚴格遞增,大於則移除。 ### q_descend > commit [7865873](https://github.com/hwz222/lab0-c/commit/78658738d5e96458aa670a4c0d60ac53b5541b39) 從佇列尾端開始反向走訪,維護一個 `lower_bound` 使得下個走訪對象不能 $<$ 該元素,並持續更新使佇列嚴格遞減,小於則移除。 ### mergeSort_merge > commit [f41ce0b](https://github.com/hwz222/lab0-c/commit/f41ce0b6fb13a4f7c23ad92660ca38f473f06dde) 使用兩個指標 `ptr1` `ptr2` 紀錄兩個佇列開頭, `merged` 指標作為要被合併到的佇列,比較開頭大小後將較小/大的元素移到 `merged` 的尾端,當兩佇列指標都走訪完畢,兩佇列就完成合併至 `merged` 佇列,最後使用 `list_splice` 將 `merged` 將所有佇列元素連結回第一個佇列完成合併。 改進方向:使用雙重指標進行合併,減少不必要記憶體空間 ### q_sort > commit [f41ce0b](https://github.com/hwz222/lab0-c/commit/f41ce0b6fb13a4f7c23ad92660ca38f473f06dde) 使用快慢指標找出中間元素,並使用巨集 `list_cut_position` 將原佇列遞迴分割成兩部份,分別進行排序,再使用 `mergeSort_merge` 將兩佇列合併。 改進方向:引入 `list_sort` 實做 bottom up 非遞迴排序 ### q_merge > commit [703ba03](https://github.com/hwz222/lab0-c/commit/703ba03dad8d624acf58d1fd8ef9f6886bf704e6) 傳入參數 `head` 指向第一個佇列的開頭,使用 `Mergesort_merge` 逐步合併,將第二個佇列合併至第一個,直到剩下一個佇列就完成所有佇列的合併。