## 介紹
根據作業 [測驗 1](https://hackmd.io/@sysprog/linux2024-quiz1#%E6%B8%AC%E9%A9%97-1) 介紹的 qsort 演算法原理介紹
:::warning
此處提到的方法是以 swap 為主體,
利用 `L` 與 `R` 去**紀錄需交換的數量**,
再用 `begin[]` 與 `end[] `**作為堆疊,用來紀錄比較的範圍。**
假定下方是起始的陣列內容。
採取 `head` 作為 `pivot`,
並且
`L` 會從左邊開始掃,紀錄有多少值小於 pivot,
`R` 從右邊開始,紀錄有多少值大於 pivot。
:::
### step 0
在 `void quick_sort(node_t **list)` 中會用到下列變數
* `*begin[max_level] `和 `*end[max_level] `:當成堆疊,用來紀錄
0. `*list` 的頭節點,尾端節點
1. `pivot` 節點
2. `*left` 串列的**頭端**和**尾端**節點
3. `*right` 串列的**頭端**和**尾端**節點
* R 變數:會從**右邊**開始掃 `*list` 串列
* L 變數:會從**左邊**開始掃 `*list `串列
在 L 掃 `*list `串列過程中,會用到
`*left` 串列:用來紀錄有多少節點值小於 pivot,
`*right` 串列:用來紀錄有多少節點值大於 pivot。
```c
void quick_sort(node_t **list)
{
int n = list_length(list);
int value;
int i = 0;
int max_level = 2 * n;
node_t *begin[max_level], *end[max_level];// begin 和 end 陣列是用來存節點位址
node_t *result = NULL, *left = NULL, *right = NULL;
begin[0] = *list;//頭節點位址
end[0] = list_tail(list);//末端節點位址
```
```graphviz
digraph {
a[label="NULL", shape=box]
b[label="NULL", shape=box]
node [shape=box]
graph [rankdir = "LR"];
4 -> 1 -> 3 -> 5 -> 2 -> 7;
node [shape=plaintext];
pivot -> 4;
L -> 4;
rank=same {4 pivot L};
R -> 7;
rank=same {7 R};
list->4;
left->a;
right->b;
}
```
陣列的 Graphviz 畫法參考
[Graphviz draw array data structure](https://stackoverflow.com/questions/40541091/graphviz-draw-array-data-structure)
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"" -> "begin[i]" [color=white];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
pointers [label="<f0> begin[0] = *list; | <f1> | <f2> | <f3> | <f4> | <f5> ", color=white];
begin [label="<f0> 4 | <f1> [1] | <f2> [2] | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; ""; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers:f0 -> begin:f0;
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
""->"end[i]" [color=white];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
pointers [label="<f0> end[0] = list_tail(list); | <f1> | <f2> | <f3> | <f4> | <f5> ", color=white];
end [label="<f0> 7 | <f1> [1] | <f2> [2] | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; ""; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers:f0 -> end:f0;
}
```
---
### step 1
從`*begin[max_level] `取出一個 index 矩陣資料`begin[i]` 當成 L 節點
從`*end[max_level] `取出一個 index 矩陣資料`end[i] `當成 R 節點
```c
while (i >= 0) {
node_t *L = begin[i], *R = end[i];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
```
當 `i=0;`時為例
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]" [color=white];
pointers [label="4", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 4 | <f1> [1] | <f2> [2] | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f0[label=" *L = begin[0]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]" [color=white];
pointers [label="7", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 7 | <f1> [1] | <f2> [2] | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; begin }
edge [color=blue];
pointers-> begin:f0[label=" *R = end[0]",dir=back];
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
1 -> 3 -> 5 -> 2 -> 7;
node [shape=plaintext, fontsize=18];
"P"->1 [color=white];
node [shape=box]
pivot[label="pivot", shape=box,color=white];
pivot->4[color=white];
rank=same{4 1};
4->1;
}
```
---
### step 2.
比較的**節點n value**(`n->value` ) 和 **pivot 節點的 value** (`value` )
`list_add(n->value > value ? &right : &left, n);`
* `n->value` 值大於 pivot ,則放在 `right` 串列(代表 pivot的右邊)
* 若 `n->value` 值大於 pivot 為否,代表 `n->value` 值小於 pivot ,則放在 `left `串列(代表 pivot 的左邊)
```c
while (p) {
node_t *n = p;
p = p->next;//CCCC
list_add(n->value > value ? &right : &left, n);
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
1 -> 3 -> 5 -> 2 -> 7;
node [shape=plaintext, fontsize=18];
"P"->1 [color=white];
node [shape=box]
pivot[label="pivot", shape=box,color=white];
pivot->4[color=white];
rank=same{pivot 4 7};
4->7;
}
```
:::danger
```c
void list_add(node_t **list, node_t *node_t)
{
node_t->next = *list;//因為新節點的 next 位址都是指向 list head,所以串鍊結時是倒著串
*list = node_t;
}
```
因此經過 `list_add(n->value > value ? &right : &left, n);` 後
right鍊結:
* 頭端節點為 7
* 尾端節點為 5
left鍊結:
* 頭端節點為 2
* 尾端節點為 1
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
1->3->2[dir=back];
5->7[dir=back];
node [shape=plaintext, fontsize=18];
"left"->1[color=white];
"right"->5[color=white];
}
```
:::
---
### step3.
比較結束,將 `begin[i]` 與 `end[i]` 儲存 `left` 的**頭**與**尾**,
`begin[i+1]` 與 `end[i+1]` 儲存 `pivot `,
`begin[i+2]` 與 `end[i+2]` 儲存 `right` 的**頭**與**尾**,
將 i 設為 i + 2 ,也就是堆疊的最上層
```c
if (L != R) {
...
begin[i] = left;//將 begin[i] 設為 left 的頭
end[i] = list_tail(&left) ;//DDDD end[i] 設為 left 的尾
begin[i + 1] = pivot;//begin[i+1] 與 end[i+1] 設為 pivot ,
end[i + 1] = pivot;
begin[i + 2] = right;//begin[i+2] 與 end[i+2] 設為 right 的頭與尾,將 i 設為 i + 2
//,也就是堆疊的最上層,
end[i + 2] =list_tail(&right) ;//EEEE
left = right = NULL;
i += 2;
} else {
if (L)
list_add(&result, L);
i--;
}
```
當 `i=0;`時為例
```c
begin[0] = left;//將 begin[i] 設為 left 的頭
end[0] = list_tail(&left) ;//DDDD end[i] 設為 left 的尾
begin[1] = pivot;//begin[i+1] 與 end[i+1] 設為 pivot ,
end[1] = pivot;
begin[2] = right;//begin[i+2] 與 end[i+2] 設為 right 的頭與尾,將 i 設為 i + 2
//,也就是堆疊的最上層,
end[2] =list_tail(&right) ;//EEEE
left = right = NULL;
i += 2;
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
//"end[i]" ->"begin[i]" [color=white];
"begin[i]"->"end[i]" [color=white];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 7 (right 頭) | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (right 尾) | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "end[i]"; end }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
}
```
---
## 單步執行後面程式過程
### 當 i=2 時
#### step1
```c
while (i >= 0) {
node_t *L = begin[2], *R = end[2];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]"[color=white];
pointers [label="5", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (right 尾) | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers-> end:f2[label=" *R = end[2]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]"[color=white];
pointers [label="7", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 7 (right 頭) | <f3> [3] | <f4> [4] | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f2[label=" *L=begin[2]",dir=back];
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
7->5;
node [shape=plaintext, fontsize=18];
"L"->7 [color=white];
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
rank=same{pivot 5 7};
7->5;
pivot[label="pivot", shape=box,color=white];
rank=same{pivot 7};
pivot->7[color=white];
node [shape=plaintext, fontsize=18];
"p"->5 [color=white];
}
```
#### step2
```c
while (p) {
node_t *n = p;
p = p->next;//CCCC
list_add(n->value > value ? &right : &left, n);
}
```
:::danger
```c
void list_add(node_t **list, node_t *node_t)
{
node_t->next = *list;//因為新節點的 next 位址都是指向 list head,所以串鍊結時是倒著串
*list = node_t;
}
```
因此經過 `list_add(n->value > value ? &right : &left, n);` 後
right鍊結:
* 頭端節點為 NULL
* 尾端節點為 NULL
left鍊結:
* 頭端節點為 5
* 尾端節點為 5
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
5;
node [shape=plaintext, fontsize=18];
"left"->5;
"right"->"NULL";
}
```
:::
#### step3
當 `i=2;`時為例
```c
begin[2] = left;//將 begin[i] 設為 left 的頭
end[2] = list_tail(&left) ;//DDDD end[i] 設為 left 的尾
begin[3] = pivot;//begin[i+1] 與 end[i+1] 設為 pivot ,
end[3] = pivot;
begin[4] = right;//begin[i+2] 與 end[i+2] 設為 right 的頭與尾,將 i 設為 i + 2
//,也就是堆疊的最上層,
end[4] =list_tail(&right) ;//EEEE
left = right = NULL;
i += 2;
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
//"end[i]" ->"begin[i]" [color=white];
"begin[i]"->"end[i]" [color=white];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 5 (left 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (left 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "end[i]"; end }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
}
```
下一步執行 i=4 的情況
---
### 當 i=4 時
#### step1
```c
while (i >= 0) {
node_t *L = begin[4], *R = end[4];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
} else {
if (L)
list_add(&result, L);
i--;
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]"[color=white];
pointers [label="NULL", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (left 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers-> end:f4[label=" *R = end[4]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]"[color=white];
pointers [label="NULL", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 5 (left 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f4[label=" *L=begin[4]",dir=back];
}
```
:::warning
因為 `node_t *L = begin[4], *R = end[4];` **取到的值都是 NULL ( L==R 的條件)**
所以直接跳到
```c
else {
if (L) /// L 為 null 所以不用 list_add()
list_add(&result, L);
i--;
}
```
執行完 `i--`,回到 `i=3` 的情況,重新執行 step1-step3 步驟
:::
---
### 回到 i=3 時
#### step1
```c
while (i >= 0) {
node_t *L = begin[3], *R = end[3];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
} else {
if (L)
list_add(&result, L);
i--;
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]"[color=white];
pointers [label="7", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (left 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers-> end:f3[label=" *R = end[3]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]"[color=white];
pointers [label="7", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 5 (left 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f3[label=" *L=begin[3]",dir=back];
}
```
:::warning
因為 `node_t *L = begin[3], *R = end[3];` **取到的值都是 節點7 ( L==R 的條件)**
所以直接跳到
```c
else {
if (L) /// L 為 節點7 所以執行 list_add()
list_add(&result, L);
i--;
}
```
`result` 插入 L 節點
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
7;
node [shape=plaintext, fontsize=18];
rank=same{"result " 7};
}
```
執行完 `i--`,回到 `i=2` 的情況,重新執行 step1-step3 步驟
:::
---
### 回到 i=2 時
#### step1
```c
while (i >= 0) {
node_t *L = begin[2], *R = end[2];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
} else {
if (L)
list_add(&result, L);
i--;
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]"[color=white];
pointers [label="5", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (left 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers-> end:f2[label=" *R = end[2]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]"[color=white];
pointers [label="5", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 5 (left 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f2[label=" *L=begin[2]",dir=back];
}
```
:::warning
因為 `node_t *L = begin[2], *R = end[2];` **取到的值都是 節點5 ( L==R 的條件)**
所以直接跳到
```c
else {
if (L) /// L 為 節點5 所以執行 list_add()
list_add(&result, L);
i--;
}
```
`result` 插入 L 節點
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
7->5[dir=back];
node [shape=plaintext, fontsize=18];
rank=same{"result" 7};
}
```
執行完 `i--`,回到 `i=1` 的情況,重新執行 step1-step3 步驟
:::
---
### 回到 i=1 時
#### step1
```c
while (i >= 0) {
node_t *L = begin[1], *R = end[1];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
} else {
if (L)
list_add(&result, L);
i--;
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]"[color=white];
pointers [label="4", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (left 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers-> end:f1[label=" *R = end[1]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]"[color=white];
pointers [label="4", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 5 (left 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f1[label=" *L=begin[1]",dir=back];
}
```
:::warning
因為 `node_t *L = begin[1], *R = end[1];` **取到的值都是 節點4 ( L==R 的條件)**
所以直接跳到
```c
else {
if (L) /// L 為 節點4 所以執行 list_add()
list_add(&result, L);
i--;
}
```
`result` 插入 L 節點
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
7->5->4[dir=back];
node [shape=plaintext, fontsize=18];
rank=same{"result " 7};
}
```
執行完 `i--`,回到 `i=1` 的情況,重新執行 step1-step3 步驟
:::
---
### 回到 i=0 時
#### step1
```c
while (i >= 0) {
node_t *L = begin[i], *R = end[i];//將 L 與 R 初始化成 begin[0] 和 end[0] ,分別對應鏈結串列的頭跟尾
if (L != R) {
node_t *pivot = L;//選定 L 為 pivot
value = pivot->value;//記下 pivot 節點的 value
node_t *p = pivot->next;//*p 為pivot的下一個節點位址
//因為*p已經先備份好 pivot的下一個節點了
pivot->next = NULL;//因此原來的 pivot先把next 填 NULL,避免誤觸 pivot移動到下個節點
...
```
當 `i=0;`時
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"R" ->"end[i]"[color=white];
pointers [label="1", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
end [label="<f0> 1 (left 尾) | <f1> pivot 4 | <f2> 5 (left 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "R"; pointers }
{ rank=same; "end[i]"; end }
edge [color=blue];
pointers-> end:f0[label=" *R = end[0]",dir=back];
}
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
"L" ->"begin[i]"[color=white];
pointers [label="2", color=black,shape=box];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 2 (left頭) | <f1> pivot 4 | <f2> 5 (left 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "L"; pointers }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
pointers-> begin:f0[label=" *L=begin[0]",dir=back];
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
2->3->1;
node [shape=plaintext, fontsize=18];
"L"->2 [color=white];
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
2;
node [shape=plaintext, fontsize=18];
"pivot"->2 [color=white];
}
```
#### step2
```c
while (p) {
node_t *n = p;
p = p->next;//CCCC
list_add(n->value > value ? &right : &left, n);
}
```
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
3->1;
node [shape=plaintext, fontsize=18];
"p"->3 [color=white];
node [shape=box]
pivot[label="pivot", shape=box,color=white];
pivot->2[color=white];
rank=same{pivot 2 3};
2->3;
}
```
:::danger
```c
void list_add(node_t **list, node_t *node_t)
{
node_t->next = *list;//因為新節點的 next 位址都是指向 list head,所以串鍊結時是倒著串
*list = node_t;
}
```
因此經過 `list_add(n->value > value ? &right : &left, n);` 後
right鍊結:
* 頭端節點為 1
* 尾端節點為 1
left鍊結:
* 頭端節點為 3
* 尾端節點為 3
pivot:
* 節點2
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
3;
1;
node [shape=plaintext, fontsize=18];
"right"->3;
"left"->1;
}
```
:::
#### step3
當 `i=0;`時為例
```c
begin[0] = left;//將 begin[0] 設為 left 的頭
end[0] = list_tail(&left) ;//DDDD end[0] 設為 left 的尾
begin[1] = pivot;//begin[1] 與 end[1] 設為 pivot ,
end[1] = pivot;
begin[2] = right;//begin[2] 與 end[2] 設為 right 的頭與尾,將 i 設為 i + 2
//,也就是堆疊的最上層,
end[2] =list_tail(&right) ;//EEEE
left = right = NULL;
i += 2;
```
```graphviz
digraph {
node [shape=plaintext, fontcolor=red, fontsize=18];
//"end[i]" ->"begin[i]" [color=white];
"begin[i]"->"end[i]" [color=white];
node [shape=record, fontcolor=black, fontsize=14, width=4.75, fixedsize=true];
begin [label="<f0> 1 (left頭) | <f1> pivot 2 | <f2> 3 (right 頭) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
end [label="<f0> 1 (left 尾) | <f1> pivot 2 | <f2> 3 (right 尾) | <f3> pivot 7 | <f4> NULL | <f5> [5]", color=blue, fillcolor=lightblue, style=filled];
{ rank=same; "end[i]"; end }
{ rank=same; "begin[i]"; begin }
edge [color=blue];
}
```
### 回到 i=2 時
#### step1
:::warning
因為
```c
*L = begin[2]~begin[0];
*R = end[2]~end[0];
```
都是 `L==R` 相同節點值
```c
else {
if (L) /// L 為 節點4 所以執行 list_add()
list_add(&result, L);
i--;
}
```
所以都是重複執行,將 L 節點(`begin[2]~begin[0];` ) 依序插入到 `result` 串列中,變成
```graphviz
digraph {
node [shape=box]
graph [rankdir = "LR"];
7->5->4->3->2->1[dir=back];
node [shape=plaintext, fontsize=18];
rank=same{"result 頭" 1};
rank=same{"result 尾" 7};
}
```
:::
---
## 總結
:::success
* Quick Sort 分類成兩個串列
1. 大於 pivot 的 right 串列
2. 小於 pivot 的 left 串列
* 本來在 Quick Sort 遞迴版,
只要將這兩個串列再代入 `void quick_sort(node_t **list)`
就能進一步排序 right 串列,left 串列裡面的資料大小
* 但是我們 Quick Sort 是**用 "非遞迴" 寫法,**
所以**得拆成兩個子陣列排序**
例如 拆成
`left` 陣列 [5,7]
`right` 陣列 [1,3,2]
兩個子陣列得另外排序
* 幸好我們是用 link list 串列寫法,
**所以不用再額外分配連續空間的子陣列空間**
left 子串列 5-->7
right 子串列 1-->3-->2
* **為了方便管理每個子串列
因此用陣列 `begin[]` 和 `end[]`** 來管理每個子陣列的
head 節點,尾節點,pivot
才能方便調閱每個子串列
:::