contributed by < brianlin314
>
不用抄題目,開發紀錄著重於你的想法和過程中遭遇到的問題,紀錄和討論才是主體,程式碼僅作搭配說明的用途。
學生收到
Image Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
遞迴版 quick sort
,使用 Divide and Conquer
的概念。
quick sort
實做流程:
pivot
pivot
的資料放在左邊,大於 pivot
的資料放在右邊首先,會先判斷 list
是否有節點存在,抑或是只存在一個節點。
list_less
, list_greater
分別代表兩個不同 list
的 head
,前者放入比 Pivot
小的節點,後者,反之。
pivot
設為該 list
的第一個節點。
所以使用 list_first_entry
來取得 list
的第一個節點的 entry
。
不用提及行號,可斟酌列舉相關程式碼來解說。避免「舉燭」,你應該思考:要是我來實作這樣的程式碼,我會怎麼做。
使用 list_for_each_entry_safe
來走訪整個 list
,接著使用比較函式 cmpint
來判斷節點數值跟 pivot
的大小
pivot
,用 list_move_tail
將該節點從原 list 移動到 list_less
的尾端。pivot
,用 list_move_tail
將改節點移動到 list_greater
的尾端。接著持續不斷對 &list_less
與 &list_greater
進行遞迴,直到無法切割為止。
最後,將 &list_less
先串接到 head
,再將 &list_greater
接到該 list
的尾端。
因為上述程式碼每次皆選取 list
的第一個節點作為 pivot
,這會遇到一個問題,就是當該 list
已經是由大到小或是由小到大排序時,會導致每次選到的 pivot
都剛好是最大值或最小值,而沒辦法均分 list_less
和 list_greater
,若遇到該極端狀況,上述程式碼的時間複雜度就是 。
因此可以透過更改選擇 pivot
的方法,去改進上述的程式碼,大概分為以下三種方法:
一開始即宣告一個 stack
,代表之後的實作皆與之有關,並且依照其定義的大小,使用 INIT_LIST_HEAD
初始化,接著將整條未排列的 list
放到 stack
裡。
再宣告一個 partition
,作用是每次迴圈中都會存放 stack
top 中的 list
。
需特別注意 ++top
與 top++
的差別,因為自己習慣用 i++
了,所以常常忘記區分這兩種 case。
每次進入迴圈時,都會將 stack
top 中的 list
放到 partition
中,並進行以下 if-else 判斷。
若 partition
有大於等於兩個節點,則通過該條件式,並且與 測驗一
的實作方法相同,pivot
選為該 list
的第一個節點,使用 list_for_each_entry_safe
走訪整個 list
並將比 pivot
小的節點放到 list_less
中,反之,則放到 list_greater
。
先將 pivot
放到 list_less
的尾端,因為 list_less
裡面的節點值皆比 pivot
小。
再來要將 list_less
和 list_greater
再次放到 stack
中,由於是要加入到 stack
中,所以要先放入 list_greater
,再放入 list_less
,這樣才符合 FILO,等等拿出來的順序才是由小到大。
若 partition
只有一個節點的話,會將 partition
放回 stack
中。
while
迴圈會不斷從 stack
中 pop list
出來,並且插入到 head
的尾端,直到該 list
不是只有一個節點存在或該 stack
為空為止。