# 2024q1 Homework1 (lab0) contributed by < `will-in-nc` > ## 開發環境 ```shell $ gcc --version gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Address sizes: 36 bits physical, 48 bits virtual Byte Order: Little Endian CPU(s): 12 On-line CPU(s) list: 0-11 Vendor ID: AuthenticAMD Model name: AMD Ryzen 5 5600X 6-Core Processor CPU family: 25 Model: 33 Thread(s) per core: 2 Core(s) per socket: 6 Socket(s): 1 Stepping: 0 CPU max MHz: 3701.0000 CPU min MHz: 0.0000 BogoMIPS: 7402.00 ``` ## 指定的佇列操作 ### `q_insert_head`, `q_insert_tail` :::danger 避免過多的中英文混用,已有明確翻譯詞彙者,例如「鏈結串列」(linked list) 和「佇列」(queue),就使用該中文詞彙,英文則留給變數名稱、人名,或者缺乏通用翻譯詞彙的場景。 ::: <s>這兩個function的內容幾乎相同</s> ,只有最後是呼叫`list_add`或是`list_add_tail`的區別 :::warning 對應到數學公式,function 應翻譯為「函數」,後者就像是台「機器」,它能夠將集合 A 裡面的每個元素「唯一地」對應到集合 B 裡的一個元素。但在 C 語言一類的通用程式語言中,function 未必具備數學函數的意涵,也就是缺乏「唯一對應」的特性,例如對於 [time](https://man7.org/linux/man-pages/man2/time.2.html) 來說,給定同樣的輸入,卻會在不同時間點得到不同的結果,在 C 語言一類的通用程式語言中,甚至可省略傳回值,後者就不符合「函數」的行為。因此,當在通用的程式設計中,function 應翻譯為「函式」,表示「衍生自數學函數的常式 (routine)」。另外,對於更一般的描述,function 會指「功能」一類的意涵,應合適挑選譯詞。 $\to$ [資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary) ::: ```c /* Insert an element at head of queue */ bool q_insert_head(struct list_head *head, char *s) { if (!head) return false; element_t *e_new = (element_t *) malloc(sizeof(element_t)); if (!e_new) return false; e_new->value = (char *) malloc(sizeof(char) * (strlen(s) + 1)); if (!e_new->value) { free(e_new); return false; } INIT_LIST_HEAD(&e_new->list); snprintf(e_new->value, strlen(s) + 1, "%s", s); list_add(&e_new->list, head); return true; } ``` ### q_free ```c /* Free all storage used by queue */ void q_free(struct list_head *head) { if (!head) return; element_t *e, *next; list_for_each_entry_safe (e, next, head, list) { q_release_element(e); } free(head); } ``` 一開始我不斷遇到以下錯誤 ``` ERROR: Corruption detected in block with address 0x7fffd9c7ce10 when attempting to free it ``` 這個錯誤發生的原因是我在新增節點中的`value`時要求的記憶體大小和複製過去的字串大小不一致 ### `q_remove_tail`, `q_remove_tail` 這兩個function的內容幾乎相同,只有目標是`head->next`或是`head->prev`的區別 ```c /* Remove an element from head of queue */ element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { if (!head) return NULL; if (list_empty(head)) return NULL; struct list_head *target_list = head->next; element_t *target_e = list_entry(target_list, element_t, list); list_del_init(target_list); if (sp != NULL) snprintf(sp, bufsize, "%s", target_e->value); return target_e; } ``` ### q_reverse ```c /* Reverse elements in queue */ void q_reverse(struct list_head *head) { if (!head) return; if (list_empty(head)) return; struct list_head *l_head = head->next; while (l_head->next != head) { list_move_tail(head->prev, l_head); } } ``` 標記出原本佇列的第一個節點後,不斷地將佇列的最後一個節點移到開頭,直到原本的第一個節點成為最後一個節點 ### `q_reverseK` ```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) return; if (list_empty(head)) return; struct list_head *l_head = head->next; int times = q_size(head) / k; for (int j = 0; j < times; j++) { struct list_head *prev = l_head->prev, *target = l_head->next; for (int i = 1; i < k; i++) { struct list_head *temp = target; target = target->next; list_move(temp, prev); } l_head = l_head->next; } } ``` ### `q_swap` ```c /* Swap every two adjacent nodes */ void q_swap(struct list_head *head) { // https://leetcode.com/problems/swap-nodes-in-pairs/ if (!head) return; if (list_empty(head)) return; q_reverseK(head, 2); } ``` `q_swap`可以直接<s>調用</s> 呼叫 `q_reverseK`來實作 ### `q_sort` ```c struct list_head *merge_sort(struct list_head *head, struct list_head *tail, int size, bool descend) { if (size == 1) return head; struct list_head *mid = head; int len = 0; while (len < size / 2) { mid = mid->next; len++; } int len2 = size - len; struct list_head *l1 = merge_sort(head, mid->prev, len, descend), *l2 = merge_sort(mid, tail, len2, descend), *prev = l1->prev, *lhead = prev; element_t *e1 = list_entry(l1, element_t, list), *e2 = list_entry(l2, element_t, list); while (len > 0 && len2 > 0) { struct list_head *target = NULL; if ((strcmp(e1->value, e2->value) > 0) == descend) { target = &e1->list; l1 = l1->next; e1 = list_entry(l1, element_t, list); len--; } else { target = &e2->list; l2 = l2->next; e2 = list_entry(l2, element_t, list); len2--; } list_move(target, lhead); lhead = target; } return prev->next; } /* Sort elements of queue in ascending/descending order */ void q_sort(struct list_head *head, bool descend) { if (!head) return; if (list_empty(head)) return; merge_sort(head->next, head->prev, q_size(head), descend); } ``` 目前為止我還沒想出以迭代實作合併排序的方法,因此以遞迴的方式實作 ### `q_ascend`, `q_descend` ```c /* Remove every node which has a node with a strictly less value anywhere to * the right side of it */ int q_ascend(struct list_head *head) { // https://leetcode.com/problems/remove-nodes-from-linked-list/ if (!head) return 0; int len = q_size(head); if (len <= 1) return len; element_t *e = list_entry(head->prev, element_t, list); struct list_head *next = e->list.prev; while (next != head) { element_t *e_next = list_entry(next, element_t, list); if (strcmp(e->value, e_next->value) < 0) { element_t *temp = e_next; next = next->prev; list_del_init(&temp->list); q_release_element(temp); len--; } else { e = e_next; next = next->prev; } } return len; } ``` 從佇列的尾端開始檢查節點是否符合升序或降序 ### `q_merge` ```c /* Merge all the queues into one sorted queue, which is in ascending/descending * order */ int q_merge(struct list_head *head, bool descend) { // https://leetcode.com/problems/merge-k-sorted-lists/ if (!head) return 0; if (q_size(head) <= 1) return 0; queue_contex_t *main_q = list_entry(head->next, queue_contex_t, chain); queue_contex_t *q, *next; int len = q_size(main_q->q); list_for_each_entry_safe (q, next, head, chain) { if (q != main_q) { struct list_head *l1 = main_q->q->next, *l2 = q->q->next, *prev = main_q->q, *lhead = prev; int len1 = len, len2 = q_size(q->q); len += len2; list_splice_init(q->q, main_q->q); element_t *e1 = list_entry(l1, element_t, list), *e2 = list_entry(l2, element_t, list); while (len1 > 0 && len2 > 0) { struct list_head *target = NULL; if ((strcmp(e1->value, e2->value) > 0) == descend) { target = &e1->list; l1 = l1->next; e1 = list_entry(l1, element_t, list); len1--; } else { target = &e2->list; l2 = l2->next; e2 = list_entry(l2, element_t, list); len2--; } list_move(target, lhead); lhead = target; } } } return len; } ``` 利用`q_sort`中合併的部分來實作 :::danger HackMD 不是讓你張貼完整程式碼的地方,GitHub 才是!因此你在開發紀錄只該列出關鍵程式碼 (善用 diff 標示),可附上對應 GitHub commit 的超連結,列出程式碼是為了「檢討」和「便於他人參與討論」,不是用來「假裝自己有付出」 ::: :::danger 無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利) :::