2023-linux_kernel
contributed by < JoshuaLee0321
>
題目敘述如下:
struct item {
uint16_t i;
struct list_head list;
};
static inline int cmpint(const void *p1, const void *p2)
{
const uint16_t *i1 = (const uint16_t *) p1;
const uint16_t *i2 = (const uint16_t *) p2;
return *i1 - *i2;
}
而目標為利用 linux/list.h
中的 API 對快速排序法實作
divide and conquer 演算法
,而很明顯的這個演算法就會把給定的 linked list 分為兩半來處理,除此之外還要保證 stablestatic void list_sort(struct list_head *head)
{
if (list_empty(head) || list_is_singular(head))
return;
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
struct item *pivot = list_first_entry(head, AAA, BBB);
list_del(&pivot->list);
struct item *itm = NULL, *is = NULL;
CCC (itm, is, head, list) {
if (cmpint(&itm->i, &pivot->i) < 0)
DDD (&itm->list, &list_less);
else
EEE (&itm->list, &list_greater);
}
list_sort(&list_less);
list_sort(&list_greater);
list_add(&pivot->list, head);
list_splice(&list_less, head);
FFF(&list_greater, head);
}
由於知道是 quick sort,我們就可以直接填入以上AAA 到 FFF 了
struct item *pivot = list_first_entry(head, AAA, BBB);
CCC 吃四個參數,主要是為了把最前面的itm 移除掉後給list_less 或 list_greater,所以
最後的 FFF,由於是已經把 list_greater 排序好了,而很明顯這個已經排序好的 linked list 為比較大的鏈結串列,那只需要把他接在最後面,這個排序就完成了。
#define MAX_DEPTH 512
static void list_sort_nr(struct list_head *head)
{
if (list_empty(head) || list_is_singular(head))
return;
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);
while (top >= 0) {
INIT_LIST_HEAD(&partition);
list_splice_init(&stack[top--], &partition);
if (!list_empty(&partition) && !list_is_singular(&partition)) {
struct list_head list_less, list_greater;
INIT_LIST_HEAD(&list_less);
INIT_LIST_HEAD(&list_greater);
struct item *pivot =
list_first_entry(&partition, struct item, list);
list_del(&pivot->list);
INIT_LIST_HEAD(&pivot->list);
struct item *itm = NULL, *is = NULL;
GGGG (itm, is, &partition, list) {
list_del(&itm->list);
if (cmpint(&itm->i, &pivot->i) < 0)
list_move(&itm->list, &list_less);
else
list_move(&itm->list, &list_greater);
}
HHHH (&pivot->list, &list_less);
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, IIII);
if (!list_empty(&list_less))
list_splice_tail(&list_less, JJJJ);
} 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(KKKK);
list_add_tail(&tmp->list, head);
}
}
}
}
if (list_empty(head) || list_is_singular(head))
return;
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);
2023/2/28 老師我想要請問為甚麼要寫
int top = -1;
list_splice_init(head, &stack[++top]);
而不是
int top = 0;
list_splice_init(head, &stack[top]);
這樣意義何在?多做一件事情不是會多消耗資源嗎?
GGGG (itm, is, &partition, list) {
list_del(&itm->list);
if (cmpint(&itm->i, &pivot->i) < 0)
list_move(&itm->list, &list_less);
else
list_move(&itm->list, &list_greater);
}
GGGG
很明顯就是 list_for_each_entry_safe
HHHH (&pivot->list, &list_less);
if (!list_empty(&list_greater))
list_splice_tail(&list_greater, IIII);
if (!list_empty(&list_less))
list_splice_tail(&list_less, JJJJ);
HHHH
看起來就是要把 pivot
放入 list_less
中,而由於在 quicksort
中 pivot
會是 list_less
中最大的元素,故把他擺到 list_less
中的最後一位,也就是list_move_tail
list_move(&pivot->list, &list_greater)
IIII
跟 JJJJ
的意思為,當 list_greater 或是 list_less
中有東西的話,我就把他們擺入stack中,所以這邊應該要寫 &stack[++top]
如此一來可以保證 top往上移動並且不會撞到原本的元素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(KKKK);
list_add_tail(&tmp->list, head);
}
}
stack
中的元素放入 原本的 head
中,一一把排序好的元素放回head中。stack[top]
的元素拿出來之後並且初始化KKKK
很明顯的就是 &stack[top–]其實這四行程式碼可以省略成一行,變成以下:
list_move_tail(&stack[top--], head)