contributed by < lorian0738
>
zeddyuu
原本想用 lsb_release
檢查,但是我裝的版本沒有這個指令
因此改用 cat /etc/*-release
得 VERSION_ID="22.04"
確認符合作業需求
參照 Git 教學和 GitHub 設定指引 建立 GitHub 帳號與產生 SSH key
建立完公鑰下指令 clip < ~/.ssh/id_rsa.pub
時沒有這個指令
於是用 sudo apt install geomview
下載
然而還是無法取得公鑰
最後輸入 cat ~/.ssh/id_rsa.pub
再複製
(註:
git commit -a
將所有改過的 push 到 GitHub 上 commit 出去建立一個新的 git 版本
git log
看曾經 push commit 過的情況
也可以用 tig
看更詳細的
最後用 git push -u origin master
push 到 Github 上)
指令:cppcheck --version
確認 Cppcheck 2.7 符合規定
至 lab0-c fork 到自己的GitHub
再照要求 clone 下來 git clone git@github.com:lorian0738/lab0-c
使用 Visual studio Code
建立新的「空」佇列
先要 list_head 的空間,如果沒有要到就 return NULL
並利用 INIT_LIST_HEAD
建立新的佇列將下一個和前一個都指向自己
釋放佇列所佔用的記憶體
判斷若為 NULL 則直接 return,若為 empty則釋放 list_head 佔用的空間
否則利用 list_for_each_entry_safe
和 q_release_element
逐一釋放每個 node 佔用的空間
最後要記得把 head 的空間也釋放掉
在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則)
原本的寫法:
但發現忘記應該要先複製要加入的字串,否則原本的資料更動的時候會動到entry中儲存的資料,改寫成:
其中直接用 strncpy
複製可確保複製到 buffer 可容納大小且自動在結尾補上 '\0'
後來發現應該要在一開始檢查 queue 是否為 NULL ,因此在最前面補上
且在 make test 時發現有 malloc 的錯誤,檢查後發現目前的程式有可能在要 element_t 的空間時有要到,但是在要字串的空間時失敗回傳false,前面要的空間應該要被釋放掉,應分開判斷是否有要到空間改為如下:
此步將原本 7 blocks are still allocated
降為 4 ( insert_tail 的部份從 9 降到 4 ),但還是有空間沒有釋放,才想到也有可能是前面 element_t 就沒有要到空間了,但繼續做下去才檢查的話有可能後面的字串有要到空間,這時候就會沒有釋放到字串的空間,因此應該換個順序:
才解決這個問題
在 commit 的時候原本想寫 Fix malloc failure on insert_head and insert_tail
但 malloc 不合法,必須寫完整的 memory allocation 才可以
改完後超過上限 50 個 characters
因此最後提交 Fix memory allocation failure on insert
在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則)
和 q_insert_head 差不多,差別在於最後以 list_add_tail
加到最後
在佇列開頭 (head) 移去 (remove) 給定的節點
在 queue.h 的檔案中:
第二行有點看不懂 sp 是什麼,註解和前面做 insert 時的 s 一樣,但這裡應該沒有要將字串插入,所以有點困惑
但第五行的意思推測是要將移除的字串複製過去
首先判斷 queue 是否為 NULL 或空
接著用 list_first_entry
取出開頭的 entry,
並用 list_del
移走該點
最後判斷 sp 是否不是 NULL 以做複製,再回傳 element
但後來發現其實 list_del
不會釋放空間,取出開頭時不需要要一個新的空間,應該直接寫成以下就好
在佇列尾端 (tail) 移去 (remove) 給定的節點
作法和 remove_head
差不多,僅改用 list_last_entry
取出最後一個entry,且刪除節點時刪的是 head->prev 也就是最後一點
計算目前佇列的節點總量
利用 list_for_each
跑過 list 且每跑一個 node 長度就加一
移走佇列中間的節點,詳見 LeetCode 2095. Delete the Middle Node of a Linked List
確定 list 不是 NULL 或 empty 後,用前面寫過的 q_size 取得 list 的長度
接著以 list_for_each_entry_safe 跑 entry 到中間的時候做 delete 和 release
在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 LeetCode 82. Remove Duplicates from Sorted List II
首先判斷 list 若為 NULL 則回傳 false
而若 list 為 empty 或只有一點,就直接回傳true
接著利用 list_for_each_entry_safe
走訪 list,將下一個 entry 的 value 位址紀錄在 tmp,若現在的值和下一個一樣表示需要 release,且紀錄 delete 為 1,這樣走到有重複的最後一個 entry 時才會記得需要 release
而寫到這裡的時候有點混亂,有許多語法上的錯誤,以下紀錄。
原本的寫法 part 1:
當下一個為 head 就直接 return 了,忽略了最後一個 entry 可能跟前面重疊到所以也需要刪除,應該確認 delete 是否為 0 再 return
修正:
原本的寫法 part 2:
value 本身就是一個指向字串的指標了
tmp 也是指標,直接用 = 就好
在做字串比較的時候也不需要再取址
修正:
交換佇列中鄰近的節點,詳見 LeetCode 24. Swap Nodes in Pairs
利用 list_for_each_entry_safe 每跑兩個 node 就將該 node 移到前一個的前面
修正:list_move
是把 node 移到指向 node 的後面,所以這樣什麼都不會改動,應該要用 list_move_tail
以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點
在 queue.h 中:
但 q_remove_head 也沒有 allocate 或 free 所以覺得有點困惑
但總之是要將 list 整個顛倒過來
因此需要將第一個和最後一個對調、第二個和倒數第二個對調…以此類推。
首先判斷 list 若為 NULL 或 empty 或只有一個節點就直接 return
接著用 left 和 right 的指標分別指向第一點、最後一點,並往右、左跑 list
以while迴圈持續交換 left 和 right 指到的點,直到指到同一個點或是相鄰的點
類似 q_reverse,但指定每 k 個節點為一組,進行反向順序排列,詳見 LeetCode 25. Reverse Nodes in k-Group
若 head 為 NULL 或 empty 或只有一個節點或 k 為一就直接 return
接著跑迴圈並紀錄當跑了 k 個節點就需要做 reverse
跑完後有可能最後幾個加起來不到 k 個節點的沒有做 reverse 要再把它做完
更新:重看一次題目發現剩餘的不用做,也就是以下不需要
目前的寫法過於冗長,需要再想想更精簡的作法
以遞增順序來排序鏈結串列的節點,可參閱 Linked List Sort 得知實作手法
看過文章後選擇用複雜度為 O(nlogn) 的 merge sort 方法實作
一樣判斷可以直接 return 的狀況,並呼叫 mergesort_list 做 divide and conquer 中 divide 的部份 mergesort_list
找出中間點並拆成兩個 list,遞迴下去直到拆不了,再呼叫 mergeTwoLists
將兩兩合併
參考 案例探討: LeetCode 21. Merge Two Sorted Lists 使用指標的指標的方法
覺得這樣的寫法很不可思議,需要仔細思考並不搞混每個指標指在哪裡
而其中指標類型 uintptr_t 定義在 stdint.h 之中,因此加了標頭檔 #include <stdint.h>
觀摩同學的做法後發現想法錯誤的地方
找中間點的方法錯誤:原本想和前面的做法一樣從尾端和開頭開始往中間找中間點,但是在 merge 的過程中只會將next指向正確的下一個節點,而下一個節點的 prev 不會更新到正確的上一個 node,所以不能用這種方法找中間節點,應該要用快慢指標找
快慢指標(極度陽春版,若有學會如何製作更精美的版本再更新):
首先判斷 NULL 或 empty 直接回傳長度為 0
而若只有一個 node 則直接回傳 1
因為直接做不好做,可以將 list 做 reverse,接著紀錄沿途遇到的最大值,若在其後有較小的值則刪除,最後再做一次 reverse 即可
但 make test 可以發現有錯,訊息:
看起來是需要釋放空間,雖然題目寫的是 remove,於是做完 list_del 之後加了:
但這樣還是有沒釋放到的空間,發現 entry->value 沒有釋放到,改成以下:
合併所有已排序的佇列,並合而為一個新的已排序佇列,詳見 LeetCode 23. Merge k Sorted Lists
在 queue.h 中可見關於 q_merge:
而 chain 被用在 queue_contex_t 中,被用來將許多 queue 的 head 串在一起
而先前有做過 q_sort,因此先將每個 queue 合在一起,再做 sort 即可
首先若 chain 為 NULL 或 empty 回傳 0
接著原本和上面一樣想著若為 singular 回傳 1,但這裡的 head 指的是 chain 的 head,就算是 singular 也有可能有一串 queue 所以不能這樣寫
接著以 list_for_each_entry 走訪 chain,將每個 queue 接上第一個 queue
for 迴圈中也是從第一個 entry 開始走,一開始沒有注意到直接做 list_splice_init
,導致重複接了第一個 queue,應該要避免
再進行 sort 並回傳大小
目前分數:95/100
constant time 沒有過
這邊測出
--- TOTAL 100/100
最後兩行: