contributed by < CYT701 >
建立新的佇列並呼叫 malloc
動態配置記憶體,依要求此佇列的 next
與 prev
指標都指向自己,利用 list.h
中的 INIT_LIST_HEAD
來建立此 queue
list_for_each_safe
會從 l->next
開始遍歷所有節點,在每個節點都用 list_del
將節點從串列中移走並釋放記憶體,最後因為是從 l->next
開始遍歷,所以要記得釋放 l
更新為使用 list_for_each_entry_safe
,能夠對整個 element_t
作改動
不確定 char *s
是什麼用途,感覺用不到
已找出問題點, list_head
這個結構包含在 element_t
中,而 element_t
中的 value
就是 char
型態,所以在插入新元素時應考慮 element_t
而非單純的串列,故先新增一個 new_element
並取得字串 s
的長度存於 len
中,再將字串 s
複製到 new_element->value
,並將 new_element->list
加入到 head
中
尚未解決的錯誤
已解決如下
Common vulnerabilities guide for C programmers 中提到 strcpy
並不會檢查暫存器的長度,且可能會覆蓋預期目標地址的連續記憶體空間,這表示 strcpy
並不是安全的動作,應使用 strlcpy
或 strncpy
strlcpy
(較推薦的方法)
strncpy
(可用,但不一定會以 '\0' 作為字串結尾)根據上述,修改程式碼如下,加入了自定義的 strlcpy
函數
由於這兩筆測資是在測試 malloc failure ,而我的程式碼中並未對 malloc 失敗的情況進行處理,所以在每次動態配置記憶體時,如果發生錯誤就會產生 segmentation fault ,於是在每次動態配置記憶體時都加入了判斷條件檢查是否成功,即可通過測資
這邊的問題出在 new_element->value = malloc(sizeof(s));
這行,因為如果直接動態配置 sizeof(s)
則只會配置 char
的大小 8 給 new_element->value
,又因為使用 strlcpy
所以字串的最後一個字元會是 \0
,故實際印出的只有 7 個字元
修改方法則是先宣告一個 int
變數 length
,並利用 strlen
取得 s
的字串長度,再利用 new_element->value = malloc(sizeof(char) *length);
來動態配置
猜測這裡出現的問題是 strlen
的時間複雜度為 O(n)
,導致無法以常數時間執行 insert
尚未想到解法,參考其他同學的作法,有些人使用 strdup
複製字串,可以自動配置記憶體,說不定就能避免這個情況
如果要使用 strlcpy
似乎就一定要先計算出字串長度,待補充
一開始沒有檢查 head
是否為空,但後來發現如果有對空佇列進行操作時會無法處理,於是在函式開始時加上檢查條件
建立 rm_element
,利用 list_first_entry
或 list_last_entry
來取得所要求的元素,利用 list_del
將其從佇列中移走(在 queue.h
中有強調在這裡只需要 unlink 不需要 free ),再利用之前定義好的 strlcpy
將 rm_element->value
複製到 sp
利用 list_for_each
走訪所有節點,每走過一個節點就讓 count++
,回傳 count
想法很單純,就是利用前面做的 q_size
來計算出 mid
,由於是刪除中間的元素,且如果有偶數個元素時取其下界,奇數個就刪除中間的元素
打一半突然覺得怪怪的,回去看 queue.h
發現題目是要求用 0-based indexing
,並取第 ⌊n / 2⌋ 個元素為中間,所以我是用錯誤的計算方式但不小心算出正確答案,程式碼應修正如下
利用 strcmp
檢查 cur_node
和 safe
是否相同,若相同則刪除 cur_node
,由於 list_for_each_entry_safe
的定義,接下來 cur_node = safe, safe = cur_node->next
,所以不會因為刪除 cur_node
而導致錯誤
上述程式碼出現的問題是,由於題目的要求是只要字串重複的節點就要一個不留的刪除,而不是保留其中一個,所以在原本的程式碼中加入了布林值 is_dup
用以記錄 cur_node
是否為重複的字串
發現問題出在使用 strcmp
檢查 cur_node
與 safe
之前,應先檢查 safe
是否已經走到 head
,否則直接使用 safe->value
會導致 segmentation fault
利用 list_for_each_safe
遍歷整個佇列,每次都將 cur_node
移動到 safe
後面即可完成交換
在檢查的時候發現這樣做會出現問題,因為在 list_for_each_safe
中,往下一個節點前進時使用了 cur_node = safe, safe = cur_node->next
,但是在這之前由於做了 list_move(cur_node, save)
,也就是 cur_node
會在 safe->next
,此時如果作 cur_node = safe, safe = cur_node->next
,則會把 safe
與 cur_node
對調,不能達到原本預期的兩兩交換效果
故調整 list_for_each_safe
的寫法,將原本 cur_node = safe, safe = cur_node->next
改為 cur_node = cur_node->next, safe = cur_node->next
在使用 ./qtest
檢查時發現結果仍然不如預期,且只有在奇數個元素時才會出現問題,發現問題出在 for 迴圈中判斷迴圈終止條件時只判斷了 cur_node != head
,應加入 safe != head
才能夠正確判斷
利用 list_for_each_safe
走訪所有節點,每次都將當前節點移動到 head
,即可完成反轉
反轉方式與上述相同,加入了 count
來計算走過得節點數,達到 k 時使用 list_cut_position
,將佇列切開後利用前面實作的 q_reverse
反轉,再接回原本的位置上
根據 你所不知道的 C 語言: linked list 和非連續記憶體中對於 merge sort 的實作,以及 Risheng1128 , seasonwang0905 , Lichiiiii 的程式碼
使用三個 function 來完成
參照 list_for_each_entry_safe
的寫法,由 head->prev
開始反向檢查,每當 cur_element
大於 prev_element
時,就將 prev_element
刪除,但是如果直接刪除 prev_element
的話會導致 for 迴圈出現錯誤,所以在迴圈中以 temp
暫存 prev_element
,將 prev_element
指向 cur_element
,再刪除 temp
與 delete_dup
的錯誤相同,未檢查 prev_element
是否已經走到 head
,加上判斷條件就正確了