--- tags: Linux Kernal --- # 2023q1 Homework1 (lab0) contributed by < [POCHUN-CHEN](https://github.com/POCHUN-CHEN?tab=repositories) > ## Helpful Git skill I have created a note to document how to use Git. [Git基本功能教學](https://hackmd.io/bDqmgVAyS9iqDXxu5rncMw?both) ## why we should use "pointer to pointer" (indirect pointer) This can **avoid using an additional conditional statement** at the **beginning of the data**. :::spoiler Worse & better code comparision * Worse code ```c void remove_list_node(List *list, Node *target) { Node *prev = NULL; Node *current = list->head; // Walk the list while (current != target) { prev = current; current = current->next; } // Remove the target by updating the head or the previous node. if (!prev) list->head = target->next; else prev->next = target->next; } ``` * Better code ```c void remove_list_node(List *list, Node *target) { // The "indirect" pointer points to the *address* // of the thing we'll update. Node **indirect = &list->head; // Walk the list, looking for the thing that // points to the node we want to remove. while (*indirect != target) indirect = &(*indirect)->next; *indirect = target->next; } ``` ::: ### Discussing In the program I wrote for the first time, I used this code ```*indirect = (*indirect)->next;```. I think this can reduce the use of pointer once and make the program more fast. Q: why can't we replace ```indirect = &(*indirect)->next;``` in line 9 by ```*indirect = (*indirect)->next;```? A: ```head``` will be changed! See the below flow diagram: :::spoiler Wrong way Wrong way: ```*indirect = (*indirect)->next;``` ```graphviz digraph list{ rankdir=LR; node [shape=record,style=normal]; head [label = "{<left>head}"]; subgraph cluster_0 { node [shape=record]; value1 [label="{value}"]; next1 [label="{<right>next}"]; label=Node1; } subgraph cluster_1 { node [shape=record]; value2 [label="{value}"]; next2 [label="{<right>next}"]; label=Node2; } subgraph cluster_2 { node [shape=record,style=bold]; indir [label = "{indir}"]; label="Pointer to pointer"; } node [shape=none] none1 [label = ""]; edge[weight=2]; head:right:e -> next2; next1:right:e -> next2; next2:right:e -> none1; edge[weight=1]; indir -> head; } ``` ::: :::spoiler Correct way Correct way: `indirect = &(*indirect)->next;` ```graphviz digraph list{ rankdir=LR; node [shape=record,style=normal]; head [label = "{<left>head}"]; subgraph cluster_0 { node [shape=record]; value1 [label="{value}"]; next1 [label="{<right>next}"]; label=Node1; } subgraph cluster_1 { node [shape=record]; value2 [label="{value}"]; next2 [label="{<right>next}"]; label=Node2; } subgraph cluster_2 { node [shape=record,style=bold]; indir [label = "{indir}"]; label="Pointer to pointer"; } node [shape=none] none1 [label = ""]; edge[weight=2]; head:right:e -> next1; next1:right:e -> next2; next2:right:e -> none1; edge[weight=1]; indir -> next1; } ``` ::: ## List structure for linked list This is the structure diagram that I initially thought of when I first tried to understand the linked list structure. Below diagram is an example of a singly linked list. ```graphviz digraph list{ rankdir=LR; subgraph cluster_0 { node [shape=record]; addr0 [label="{Address of List head}"]; next0 [label="{next}"]; label=List_of_head; } subgraph cluster_1 { node [shape=record]; value1 [label="{value}"]; subgraph cluster_11 { node [shape=record]; addr1 [label="{Address of List 1}"]; next1 [label="{next}"]; label=List_of_Node1; } label=Node1; } subgraph cluster_2 { node [shape=record]; value2 [label="{value}"]; subgraph cluster_22 { node [shape=record]; addr2 [label="{Address of List 2}"]; next2 [label="{next}"]; label=List_of_Node2; } label=Node2; } node [shape=none] none1 [label = ""]; edge[weight=2]; indirect:right:e -> list; list:right:e -> addr0; target:right:e -> addr2; next0:right:e -> addr1; next1:right:e -> addr2; next2:right:e -> none1; edge [style="dashed"]; addr0:right:e -> next0; addr1:right:e -> next1; addr2:right:e -> next2; edge[weight=1]; } ``` :::info I have a question, why can't we put a `Node_head` structure in the first `List_of_head` ? As shown in the following figure. ```graphviz digraph list{ rankdir=LR; subgraph cluster_0 { node [shape=record]; value0 [label="{value}"]; subgraph cluster_00 { node [shape=record]; addr0 [label="{Address of List head}"]; next0 [label="{next}"]; label=List_of_head; } label=Node_head; } subgraph cluster_1 { node [shape=record]; value1 [label="{value}"]; subgraph cluster_11 { node [shape=record]; addr1 [label="{Address of List 1}"]; next1 [label="{next}"]; label=List_of_Node1; } label=Node1; } subgraph cluster_2 { node [shape=record]; value2 [label="{value}"]; subgraph cluster_22 { node [shape=record]; addr2 [label="{Address of List 2}"]; next2 [label="{next}"]; label=List_of_Node2; } label=Node2; } node [shape=none] none1 [label = ""]; edge[weight=2]; indirect:right:e -> list; list:right:e -> addr0; target:right:e -> addr2; next0:right:e -> addr1; next1:right:e -> addr2; next2:right:e -> none1; edge [style="dashed"]; addr0:right:e -> next0; addr1:right:e -> next1; addr2:right:e -> next2; edge[weight=1]; } ``` This allows for more efficient use of the `Node_head` for memory space. Anwser: [自 Head 開始,鏈結 list 各節點,個別節點皆嵌入 list_head 結構體,不過 Head 是個特例,無法藉由 container_of 巨集來找到對應的節點,因為後者並未嵌入到任何結構體之中,其存在是為了找到 list 整體。](https://hackmd.io/@sysprog/c-linked-list#Linked-list-在-Linux-核心原始程式碼) ::: ## Linked list `container_of` structure * List structure ```c struct list_head { struct list_head *prev; struct list_head *next; }; ``` * Element structure with char value ```c typedef struct { char *value; struct list_head list; } element_t; ``` ##### Note: `typedef` define a type `struct{...}` to a new name `element_t`. #### Flow chart: ![](https://i.imgur.com/kOvwKZw.png) ##### Notes: * `Head` is a special case without Node. It just use to serch for the list. * In `element_t` structure, we can know the `value` address through counting the type size to get it. This is why we should use "container_of". #### References: 1. [Linux 核心原始程式碼巨集: container_of](https://hackmd.io/@sysprog/linux-macro-containerof) 2. [linux/include/linux/list.h](https://github.com/torvalds/linux/blob/master/include/linux/list.h) ## Instructions of functions in "queue.c" :::danger Write in English more proficiently! :notes: jserv ::: Homework Request: - [X] [q_new] Create an empty queue - [x] [q_free] Free all storage used by queue - [x] [q_insert_head] Insert an element at head of queue - [x] [q_insert_tail] Insert an element at tail of queue - [x] [q_remove_head] Remove an element from head of queue - [x] [q_remove_tail] Remove an element from tail of queue - [x] [q_size] Return number of elements in queue - [X] [q_delete_mid] Delete the middle node in queue - [ ] [q_delete_dup] Delete all nodes that have duplicate string - [X] [q_swap] Swap every two adjacent nodes - [x] [q_reverse] Reverse elements in queue - [x] [q_reverseK] Reverse the nodes of the list k at a time - [x] [q_sort] Sort elements of queue in ascending order - [ ] [q_descend] Remove every node which has a node with a strictly greater value anywhere to the right side of it - [ ] [q_merge] Merge all the queues into one sorted queue, which is in ascending order ### [q_new] Create an empty queue Use `INIT_LIST_HEAD`in library`list.h` to initialize `prev` & `next`. #### Instructions: 1. Use `malloc(sizeof())`to allocate memory`new`, and check the allocated memory `new` action is succesful or not. 2. If this condition is right, use `INIT_LIST_HEAD`to initialize the `struct list_head` and return `new`. ```c static inline void INIT_LIST_HEAD(struct list_head *head) { head->next = head; head->prev = head; } ``` 4. If this condition is wrong , return Null. :::spoiler Source Code ```c /* Create an empty queue */ struct list_head *q_new() { struct list_head *new = malloc(sizeof(struc list_head)); if(new){ INIT_LIST_HEAD(new); return new; } return NULL; } ``` ::: ### [q_free] Free all storage used by queue #### Instructions: 1. Check `l` exist. 2. Declare `node` to run through whole list. 3. Use `list_del` to pick up each `del` and use `q_release_element` to delete it. :::spoiler Source Code (Version 1: Dereferenced a NULL or invalid pointerAborted) ```c /* Free all storage used by queue */ void q_free(struct list_head *l) { if (!l) return; struct list_head *curr = l->next; while (curr != l) { element_t *del = list_entry(curr, element_t, list); if (!del) return; list_del(&del->list); free(del); curr = curr->next; } free(l); } ``` ::: :::spoiler Source Code (Version 2: Dereferenced a NULL or invalid pointerAborted) ```c /* Free all storage used by queue */ void q_free(struct list_head *l) { if (!l) return; struct list_head *curr = l->next; while (curr != l) { element_t *del = list_entry(curr, element_t, list); if (!del) return; list_del(&del->list); q_release_element(del); curr = curr->next; } free(l); } ``` ::: :::danger Debug: 1. Use function `q_release_element()`, instead of `free()`. Because there are not `element` just has itself, there are `value`. 2. We didn't need to check `element_t *del` is exist. It must exist when we allocate memeory before. ```c static inline void q_release_element(element_t *e) { test_free(e->value); test_free(e); } ``` 3. `curr = curr->next;` must before `list_del(&del->list)` & `q_release_element(del)`, or `curr` will pointer a cutted list node `del`. It will pointer NULL, after we delete `del`. ::: :::spoiler Source Code (Version 3) ```c /* Free all storage used by queue */ void q_free(struct list_head *l) { if (!l) return; struct list_head *curr = l->next; while (curr != l) { element_t *del = list_entry(curr, element_t, list); // if (!del) // return; curr = curr->next; list_del(&del->list); // free(del); q_release_element(del); } free(l); } ``` ::: ### [q_insert_head] Insert an element at head of queue Use `list_add`in library`list.h` to add a node next to head. ```mermaid graph LR; head-->node-->next; head-.->next; next-->node-->head; ``` ```c static inline void list_add(struct list_head *node, struct list_head *head) { struct list_head *next = head->next; next->prev = node; node->next = next; node->prev = head; head->next = node; } ``` #### Instructions: 1. Use `malloc(sizeof())`to allocate memory`new`, and check the allocated memory `new` action is succesful or not. 2. Allocate memory to `new->value` in element_t. 3. Copy value from `s` to `new->value` 4. Use `list_add()` to add a node next to head. Debug: * If we want to storage 'l' long string, we need to use 'l+1' char to storage and put '\0' after each string. * ```strncpy(new->value, s, strlen(s));``` we don't need to add one char memory size in the third input. :::spoiler Source Code ```c /* Insert an element at head of queue */ bool q_insert_head(struct list_head *head, char *s) { element_t *new = malloc(sizeof(element_t)); if (!new) return false; int sl = strlen(s); new->value = malloc( (sl + 1) * sizeof(char)); // notice there are (strlen(s)+1) size need to allocate. if (!new->value) { free(new); return false; } strncpy(new->value, s, sl); *(new->value + sl) = '\0'; // add '\0' after string. list_add(&new->list, head); return true; } ``` ::: ### [q_insert_tail] Insert an element at tail of queue This function is similar to `q_insert_head`. The only different part is `list_add(&new->list,head)` and `list_add_tail(&new->list, head)`. ::: danger !!Segmentation fault occurred. You dereferenced a NULL or invalid pointer!! Sol: We need to check, if memory is fail allocation, free fail allocation memory. ```c if (!new->value) { free(new); return false; } ``` ::: :::spoiler Source Code ```c /* Insert an element at tail of queue */ bool q_insert_tail(struct list_head *head, char *s) { element_t *new = malloc(sizeof(element_t)); if (!new) return false; int sl = strlen(s); new->value = malloc( (sl + 1) * sizeof(char)); // notice there are (strlen(s)+1) size need to allocate. if (!new->value) { free(new); return false; } strncpy(new->value, s, sl); *(new->value + sl) = '\0'; // add '\0' after string. list_add_tail(&new->list, head); return true; } ``` ::: ### [q_remove_head] Remove an element from head of queue ```c static inline void list_del(struct list_head *node) { struct list_head *next = node->next; struct list_head *prev = node->prev; next->prev = prev; prev->next = next; #ifdef LIST_POISONING node->prev = (struct list_head *) (0x00100100); node->next = (struct list_head *) (0x00200200); #endif } ``` We need to check `head` is exist and not empty. ```c /* list_empty() - Check if list head has no nodes attached */ static inline int list_empty(const struct list_head *head) { return (head->next == head); } ``` ```c /** * list_entry() - Get the entry for this node * @node: pointer to list node * @type: type of the entry containing the list node * @member: name of the list_head member variable in struct @type * * Return: @type pointer of entry containing node */ #define list_entry(node, type, member) container_of(node, type, member) ``` #### Instructions: * bufsize-1 :::warning *(removed->value + bufsize-1) = '\0'; ::: :::danger Debug: *(sp + bufsize-1) = '\0'; ::: `removed->value` just exist in function `q_remove_head`. This is not helpful for modifying parameters. :::spoiler Source Code (version 1) ```c /* Remove an element from head of queue */ element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { if (!head || list_empty(head)) return NULL; element_t *removed = list_entry(head->next, element_t, list); if (!removed) return NULL; list_del(&removed->list); if (bufsize) { strncpy(sp, removed->value, bufsize - 1); } *(removed->value + bufsize - 1) = '\0'; return removed; } ``` ::: :::spoiler Source Code (version 2) ```c /* Remove an element from head of queue */ element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { if (!head || list_empty(head)) return NULL; element_t *removed = list_entry(head->next, element_t, list); if (!removed) return NULL; list_del(&removed->list); if (bufsize) { strncpy(sp, removed->value, bufsize - 1); } *(sp + bufsize - 1) = '\0'; return removed; } ``` ::: ### [q_remove_tail] Remove an element from tail of queue #### Instructions: :::spoiler Source Code (Version 1) ```c /* Remove an element from tail of queue */ element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize) { if (!head || list_empty(head)) return NULL; element_t *removed = list_entry(head->prev, element_t, list); if (!removed) return NULL; list_del(&removed->list); if (bufsize) { strncpy(sp, removed->value, bufsize - 1); } *(removed->value + bufsize - 1) = '\0'; return removed; } ``` ::: :::spoiler Source Code (Version 2) ```c /* Remove an element from tail of queue */ element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize) { if (!head || list_empty(head)) return NULL; element_t *removed = list_entry(head->prev, element_t, list); if (!removed) return NULL; list_del(&removed->list); if (bufsize) { strncpy(sp, removed->value, bufsize - 1); } *(sp + bufsize - 1) = '\0'; return removed; } ``` ::: ### [q_size] Return number of elements in queue ```c /* list_for_each - Iterate over list nodes*/ #define list_for_each(node, head) \ for (node = (head)->next; node != (head); node = node->next) ``` #### Instructions: 1. Check `head` is exist 2. Structure a `node` 3. Declarate `size = 0`, and iterate plus it by `list_for_each` Discussing: Why can't we use `queue_contex_t *entry` to get q_size? ```c queue_contex_t *entry = list_entry(head, queue_contex_t, q); return entry->size; ``` :::spoiler Source Code ```c /* Return number of elements in queue */ int q_size(struct list_head *head) { if (!head) return 0; struct list_head *node; int size = 0; list_for_each (node, head) size++; return size; } ``` ::: ### [q_delete_mid] Delete the middle node in queue #### Instructions: 1. Check there are not just a `head` 2. Construct two `list_head`,`rabbit`、`turtle`。 3. Check `rabbit->next`、`rabbit->next->next` is not `head` 4. Move `rabbit` two steps, and `turtle` one steps. It decides when `rabbit` arrived `head`, `turtle` will stand in the middle of list. 5. Use `list_del(turtle)` to delete `turtle`. #### Discussing: If list monents is odd, there is a middle node from 1. If list monents is even, there is a middle node from 0. e.g. * n = 6 (6+1)/2=3.5 -> 4 * n = 5 (5+1)/2=3 -> 3 >The middle node of a linked list of size n is the ⌊n / 2⌋th node from the start using 0-based indexing. :::spoiler Source Code (Version 1) ```c /* Delete the middle node in queue */ bool q_delete_mid(struct list_head *head) { // https://leetcode.com/problems/de lete-the-middle-node-of-a-linked-list/ if (!head->next) return false; struct list_head *rabbit = head->next, *turtle = head->next; while (rabbit->next != head && rabbit->next->next != head) { rabbit = rabbit->next->next; turtle = turtle->next; } turtle = turtle->next; list_del(turtle); return true; } ``` ::: Update imformation is contributed by < [Risheng1128](https://github.com/Risheng1128) > ```c while (rabbit != head && rabbit->next != head) ``` :::spoiler Source Code ```c /* Delete the middle node in queue */ bool q_delete_mid(struct list_head *head) { // https://leetcode.com/problems/de lete-the-middle-node-of-a-linked-list/ if (!head->next) return false; struct list_head *rabbit = head->next, *turtle = head->next; while (rabbit != head && rabbit->next != head) { rabbit = rabbit->next->next; turtle = turtle->next; } list_del(turtle); return true; } ``` ::: ### [q_delete_dup] Delete all nodes that have duplicate string :::spoiler Source Code ```c /* Delete all nodes that have duplicate string */ bool q_delete_dup(struct list_head *head) { // https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ return true; } ``` ::: ### [q_swap] Swap every two adjacent nodes #### Instructions: 1. Check `head` exist. 2. Declare `list_head` structure `curr`, `first`, `second`. `curr` will make the list move next two steps each generation. `first` and `second` will chanege each other. :::spoiler Source Code (Version 1: Ifinity loop) ```c /* Swap every two adjacent nodes */ void q_swap(struct list_head *head) { // https://leetcode.com/problems/swap-nodes-in-pairs/ if (!head) return; struct list_head *curr = head->next, *first = NULL, *second = NULL; while (curr != head && curr->next != head) { first = curr; second = first->next; first->next = second->next; first->prev = second; second->next = first; second->prev =curr->prev; curr = curr->next->next; } } ``` ::: :::spoiler Source Code (Version 2: Ifinity loop) ```c /* Swap every two adjacent nodes */ void q_swap(struct list_head *head) { // https://leetcode.com/problems/swap-nodes-in-pairs/ if (!head) return; struct list_head *first = head->next; while (first != head && first->next != head) { struct list_head *second = first->next; first->next = second->next; second->prev =first->prev; first->prev->next = second; second->next->prev = first; first->prev = second; second->next = first; first = first->next; } ``` ::: :::danger Debug: I didn't change the pointers `first->prev->next` & `second->next->prev`. ::: :::spoiler Source Code (Version 3) ```c /* Swap every two adjacent nodes */ void q_swap(struct list_head *head) { // https://leetcode.com/problems/swap-nodes-in-pairs/ if (!head) return; struct list_head *first = head->next; while (first != head && first->next != head) { struct list_head *second = first->next; list_del_init(first); list_add(first, second); first = first->next; } ``` ::: Better code thinking. Reference guide line[q_swap] in [2022q1 Homework1 (lab0)](https://hackmd.io/@Risheng/linux2022-lab0) 。 1. I don't need to use three `list_head` 2. It will be better declare `second` in while loop. 3. Use `list_del_init`, `list_add(first, second)`. ### [q_reverse] Reverse elements in queue #### Instructions: 1. Check `head` exist. 2. Delcare `*prev` 、 `*cuur` 、 `*next` . #### Tips: If we want to do the code in the function at lease once, we can use pointer to pointer to `NULL`. After doing the code in the function the pointer which pointers to `NULL` will get meaning, we can compare it in the function condition. :::spoiler Source Code ```c /* Reverse elements in queue */ void q_reverse(struct list_head *head) { if (!head) return; struct list_head *prev = head->prev, *curr = head, *next = NULL; while (next != head) { next = curr->next; curr->next = prev; curr->prev = next; prev = curr; curr = next; } } ``` ::: ### [q_reverseK] Reverse the nodes of the list k at a time Discussing: 1. Why we can't use`list_del_init(curr)` and `list_add(curr,&list)`, but can use `list_move(curr, &list)` ? 2. Why we can't use `list_add_tail(&list,curr)` and `list_move_tail(&list, curr)`, but can use `list_splice_tail(&list, curr)` ? 3. Is `list_move_tail(&list, curr)` better than change pointers' destinations? :::spoiler Source Code (Version 1) ```c /* Reverse the nodes of the list k at a time */ void q_reverseK(struct list_head *head, int k) { // https://leetcode.com/problems/reverse-nodes-in-k-group/ if (!head || list_empty(head)) return; struct list_head *curr = head->next, *list = NULL; while (curr != head) { struct list_head *next = NULL; for (int i = 0; i < k; i++){ next = curr->next; if (next == head){ list_add(list,curr); return; } list_del_init(curr); list_add_tail(curr,list); curr = next; } q_reverse(list); list_add(list,curr); free(list); } } ``` ::: :::spoiler Source Code (Version 2) ```c // https://leetcode.com/problems/reverse-nodes-in-k-group/ if (!head || list_empty(head)) return; struct list_head *curr = head->next; while (curr != head){ struct list_head list; INIT_LIST_HEAD(&list); for (int i = 0; i < k; i++){ struct list_head *next = curr->next; if (next == head){ //list_add(&list,curr); q_reverse(&list); list_splice_tail(&list, curr); return; } // list_del_init(curr); // list_add(curr,&list); list_move(curr, &list); curr = next; } //q_reverse(&list); // list_add_tail(&list,curr); // list_move_tail(&list, curr); list_splice_tail(&list, curr); //free(&list); } } ``` ::: ### [q_sort] Sort elements of queue in ascending order #### Instructions: ==`q_sort` has a same concept as **Merge Two Sorted Lists**.== Reference Source code [quick-sort.c](https://github.com/sysprog21/linux-list/blob/master/examples/quick-sort.c) in [sysprog21/linux-list](https://github.com/sysprog21/linux-list) programs 。 1. Check linked list in `head` has more than two entry and not empty. If not return it. 2. Use `INIT_LIST_HEAD` to initialize two list structure we made. 3. Use `list_first_entry` to get first entry to set it as pivot. 4. Delete the pivot from linked list (use `list_del`) 5. Compare through all list (`list_for_each_entry_safe`). Use `strncmp` to compare `item->value` to `pivot->value`, and seperate them to each `list_less` and `list_greater`. 6. Call `q_sort` to regress `list_less` & `list_greater`. 7. Add pivot to list 8. Splice `list_less` before pivot in list. 9. Splice `list_greater` after pivot in list. #### Debug `cmprint()` error: implicit declaration of function ‘cmpint’ Sol: use `strcmp()` to replace `cmprint()` ```c list_for_each_entry_safe(item, is, head, list) { if (cmpint(&item->value, &pivot->value) < 0) list_move_tail(&item->list, &list_less); else list_move_tail(&item->list, &list_greater); } ``` or declarate function `cmprint()` ```c 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; } ``` :::spoiler Source Code ```c /* Sort elements of queue in ascending order */ void q_sort(struct list_head *head) { struct list_head list_less, list_greater; element_t *pivot; element_t *item = NULL, *is = NULL; if (list_empty(head) || list_is_singular(head)) return; INIT_LIST_HEAD(&list_less); INIT_LIST_HEAD(&list_greater); pivot = list_first_entry(head, element_t, list); list_del(&pivot->list); list_for_each_entry_safe (item, is, head, list) { if (strcmp(item->value, pivot->value) < 0) list_move_tail(&item->list, &list_less); else list_move_tail(&item->list, &list_greater); } q_sort(&list_less); q_sort(&list_greater); list_add(&pivot->list, head); list_splice(&list_less, head); list_splice_tail(&list_greater, head); } ``` ::: ### [q_descend] Remove every node which has a node with a strictly greater value anywhere to the right side of it :::spoiler Source Code ```c /* Remove every node which has a node with a strictly greater value anywhere to * the right side of it */ int q_descend(struct list_head *head) { // https://leetcode.com/problems/remove-nodes-from-linked-list/ return 0; } ``` ::: ### [q_merge] Merge all the queues into one sorted queue, which is in ascending order **Merge k Sorted Lists** is developed by **Merge Two Sorted Lists**. There is a concept that I don't really understand. `q_sort` has a same concept as **Merge Two Sorted Lists**. ```graphviz digraph G { result[label="1|2|3|4|5|6|7|8", shape=record, height=.1] node31[label="2|5", shape=record, height=.1] node32[label="4|6|8", shape=record, height=.1] node33[label="1|7", shape=record, height=.1] node34[label="3", shape=record, height=.1,width=.4] node41[label="2|4|5|6|8", shape=record, height=.1] node42[label="1|3|7", shape=record, height=.1] subgraph cluster_3 { style=filled; color="#a6cee3"; label="sorted lists"; node32; node34; node33; node31; } node31 -> node41 node32 -> node41 node33 -> node42 node34 -> node42 subgraph cluster_2 { style=filled; color="#b2df8a"; label="merge"; node41 node41 node42 node42 node41->result node42->result } } ``` #### Instructions: 1. Check `head` is exist and not empty. 2. `list_for_each_entry` is a function which iterates over list entries. In `queue_contex_t` struct, it like a 2D list. `list_head *q` is a one component list, and `list_head chain` is a group list made by many component lists. 3. ==Cut the circular linked list== to doubly linked list. 4. Use function `mergeTwoLists` to merge two lists. 5. `list_entry` is a function which gets the entry for the node. 6. Restructure doubly linked list to circular linked list. 7. Return `q_size` of `(group_list->q)`. #### Hint: * include `<stdint.h>` ,when we use `uintptr_t`. * Function features * `mergeTwoLists` : Put two lists together, and sequence them. * `mergesort_list` : Seperate lists. * Adjust function [`mergeTwoLists()`](https://hackmd.io/@sysprog/c-linked-list#%E6%A1%88%E4%BE%8B%E6%8E%A2%E8%A8%8E-LeetCode-21-Merge-Two-Sorted-Lists) below. ```c= struct ListNode *mergeTwoLists(struct ListNode *L1, struct ListNode *L2) { c *head = NULL, **ptr = &head, **node; for (node = NULL; L1 && L2; *node = (*node)->next) { node = (L1->val < L2->val) ? &L1: &L2; *ptr = *node; ptr = &(*ptr)->next; } *ptr = (struct ListNode *)((uintptr_t) L1 | (uintptr_t) L2); return head; } ``` In line 4, `*node = (*node)->next` has different data types. `*node` is `struct ListNode*` , and `(*node)->next` is `struct linked_head*`. Change `struct ListNode*` to `struct linked_head*`, and use `list_entry()`to assigment `element_t *L1_entry`. * Compare `node = (L1_entry->value < L2_entry->value) ? &L1 : &L2` & `node = strcmp(L1_entry->value, L2_entry->value) < 0 ? &L1 : &L2`. Why `strcmp()` doesn't cause "ERROR: Not sorted in ascending order (It might because of unsorted queues are merged or there're some flaws in 'q_merge')" or "ERROR: Removed value c != expected value r"? :::spoiler Source Code * Function `mergeTwoLists()` ```c /* Merge the two lists in a one sorted list. */ struct list_head *mergeTwoLists(struct list_head *L1, struct list_head *L2) { struct list_head *head = NULL, **ptr = &head, **node; for (node = NULL; L1 && L2; *node = (*node)->next) { element_t *L1_entry = list_entry(L1, element_t, list); element_t *L2_entry = list_entry(L2, element_t, list); node = (L1_entry->value < L2_entry->value) ? &L1 : &L2; *ptr = *node; ptr = &(*ptr)->next; } *ptr = (struct list_head *) ((uintptr_t) L1 | (uintptr_t) L2); return head; } ``` * Function `q_merge()` ```c /* Merge all the queues into one sorted queue, which is in ascending order */ int q_merge(struct list_head *head) { // https://leetcode.com/problems/merge-k-sorted-lists/ if (!head || list_empty(head)) return 0; queue_contex_t *entry = list_entry(head, queue_contex_t, chain); queue_contex_t *c_next = entry->chain.next; struct list_head *q_head = c_next->q; while (c_next != head) { // struct list_head *q_curr = q_head->next; // while (q_curr->next != q_head) // { // struct list_head *q_next = q_curr->next; // list_move_tail(q_curr,entry->q); // q_curr = q_next; // } list_splice_tail(q_head,entry->q); c_next = c_next->chain.next; } q_sort(q_head); int size = q_size(q_head); return size; return 0; } ``` ::: #### Q & A :::success Experiment: ```c #include <stdio.h> int main(int argc, const char * argv[]) { int n = 5; for (int i = 0; i <= n; i++) { printf("%d\n", i); } return 0; } ``` Output: ``` 0 1 2 3 4 5 ``` That means `i++` is be done after once cycle of `for` function done. ::: :::info In [案例探討: LeetCode 21. Merge Two Sorted Lists](https://hackmd.io/YA7RMqd6RE2UL8OmmjU9PQ?view#案例探討-LeetCode-21-Merge-Two-Sorted-Lists) case, why we use `struct` in Line 1 ? It should be a function. ```c struct ListNode *mergeTwoLists(struct ListNode *L1, struct ListNode *L2) { struct ListNode *head; struct ListNode **ptr = &head; for (; L1 && L2; ptr = &(*ptr)->next) { if (L1->val < L2->val) { *ptr = L1; L1 = L1->next; } else { *ptr = L2; L2 = L2->next; } } *ptr = (struct ListNode *)((uintptr_t) L1 | (uintptr_t) L2); return head; } ``` ::: :::info 在[非遞迴的實作](https://hackmd.io/@sysprog/c-linked-list#非遞迴的實作)中,原始的迭代版 merge sort 如下: ```c= void mergesort_iter(node_t **list) { node_t *head = *list; if (!head || !head->next) return; node_t *result = NULL; node_t *stack[1000]; int top = 0; stack[top] = head; while (top >= 0) { node_t *left = stack[top--]; if (left) { node_t *slow = left; node_t *fast; for (fast = left->next; fast && fast->next; fast = fast->next->next) slow = slow->next; node_t *right = slow->next; slow->next = NULL; stack[++top] = left; stack[++top] = right; } else result = mergeTwoLists(result, stack[top--]); } *list = result; } ``` 第12行程式碼意義為何? :::