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
的資料放在右邊if (list_empty(head) || list_is_singular(head))
return;
首先,會先判斷 list
是否有節點存在,抑或是只存在一個節點。
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
list_less
, list_greater
分別代表兩個不同 list
的 head
,前者放入比 Pivot
小的節點,後者,反之。
struct item *pivot = list_first_entry(head, item, list);
pivot
設為該 list
的第一個節點。
所以使用 list_first_entry
來取得 list
的第一個節點的 entry
。
不用提及行號,可斟酌列舉相關程式碼來解說。避免「舉燭」,你應該思考:要是我來實作這樣的程式碼,我會怎麼做。
struct item *itm = NULL, *is = NULL;
list_for_each_entry_safe (itm, is, head, list) {
if (cmpint(&itm->i, &pivot->i) < 0)
list_move_tail (&itm->list, &list_less);
else
list_move_tail (&itm->list, &list_greater);
}
使用 list_for_each_entry_safe
來走訪整個 list
,接著使用比較函式 cmpint
來判斷節點數值跟 pivot
的大小
pivot
,用 list_move_tail
將該節點從原 list 移動到 list_less
的尾端。pivot
,用 list_move_tail
將改節點移動到 list_greater
的尾端。list_add(&pivot->list, head);
list_splice(&list_less, head);
list_splice_tail(&list_greater, head);
接著持續不斷對 &list_less
與 &list_greater
進行遞迴,直到無法切割為止。
最後,將 &list_less
先串接到 head
,再將 &list_greater
接到該 list
的尾端。
因為上述程式碼每次皆選取 list
的第一個節點作為 pivot
,這會遇到一個問題,就是當該 list
已經是由大到小或是由小到大排序時,會導致每次選到的 pivot
都剛好是最大值或最小值,而沒辦法均分 list_less
和 list_greater
,若遇到該極端狀況,上述程式碼的時間複雜度就是 \(O(n^2)\)。
因此可以透過更改選擇 pivot
的方法,去改進上述的程式碼,大概分為以下三種方法:
struct list_head stack[MAX_DEPTH];
for (int i = 0; i < MAX_DEPTH; i++)
INIT_LIST_HEAD(&stack[i]);
int top = -1;
list_splice_init(head, &stack[++top]);
struct list_head partition;
INIT_LIST_HEAD(&partition);
一開始即宣告一個 stack
,代表之後的實作皆與之有關,並且依照其定義的大小,使用 INIT_LIST_HEAD
初始化,接著將整條未排列的 list
放到 stack
裡。
再宣告一個 partition
,作用是每次迴圈中都會存放 stack
top 中的 list
。
需特別注意 ++top
與 top++
的差別,因為自己習慣用 i++
了,所以常常忘記區分這兩種 case。
while (top >= 0) {
INIT_LIST_HEAD(&partition);
list_splice_init(&stack[top--], &partition);
...
}
每次進入迴圈時,都會將 stack
top 中的 list
放到 partition
中,並進行以下 if-else 判斷。
if (!list_empty(&partition) && !list_is_singular(&partition))
若 partition
有大於等於兩個節點,則通過該條件式,並且與 測驗一
的實作方法相同,pivot
選為該 list
的第一個節點,使用 list_for_each_entry_safe
走訪整個 list
並將比 pivot
小的節點放到 list_less
中,反之,則放到 list_greater
。
list_move_tail (&pivot->list, &list_less);
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, &stack[++top]);
if (!list_empty(&list_less))
list_splice_tail(&list_less, &stack[++top]);
先將 pivot
放到 list_less
的尾端,因為 list_less
裡面的節點值皆比 pivot
小。
再來要將 list_less
和 list_greater
再次放到 stack
中,由於是要加入到 stack
中,所以要先放入 list_greater
,再放入 list_less
,這樣才符合 FILO,等等拿出來的順序才是由小到大。
else {
top++;
list_splice_tail(&partition, &stack[top]);
while (top >= 0 && list_is_singular(&stack[top])) {
struct item *tmp =
list_first_entry(&stack[top], struct item, list);
list_del(&tmp->list);
INIT_LIST_HEAD(&stack[top--]);
list_add_tail(&tmp->list, head);
}
}
若 partition
只有一個節點的話,會將 partition
放回 stack
中。
while
迴圈會不斷從 stack
中 pop list
出來,並且插入到 head
的尾端,直到該 list
不是只有一個節點存在或該 stack
為空為止。
or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing