# 2022q1 Homework1 (lab0) contributed by < `qwer312132` > ## 實驗環境 ```shell $ gcc --version gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 39 bits physical, 48 bits virtual CPU(s): 8 On-line CPU(s) list: 0-7 Thread(s) per core: 2 Core(s) per socket: 4 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 142 Model name: Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz Stepping: 12 CPU MHz: 2100.000 CPU max MHz: 4200.0000 CPU min MHz: 400.0000 BogoMIPS: 4199.88 Virtualization: VT-x L1d cache: 128 KiB L1i cache: 128 KiB L2 cache: 1 MiB L3 cache: 6 MiB NUMA node0 CPU(s): 0-7 ``` ## [作業要求](https://hackmd.io/@sysprog/2022-lab0) * `q_new`: 建立新的「空」佇列; * `q_free`: 釋放佇列所佔用的記憶體; * `q_insert_head`: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則); * `q_insert_tail`: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則); * `q_remove_head`: 在佇列開頭 (head) 移去 (remove) 給定的節點; * `q_release_element`: 釋放特定節點的記憶體空間; * `q_size`: 計算目前佇列的節點總量; * `q_delete_mid`: 移走佇列中間的節點,詳見 [LeetCode 2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/) * `q_delete_dup`: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 [LeetCode 82. Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/) * `q_swap`: 交換佇列中鄰近的節點,詳見 [LeetCode 24. Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) * `q_reverse`: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點; * `q_sort`: 以==遞增順序==來排序鏈結串列的節點,可參閱 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) 得知實作手法; ## queue.c 實作 :::danger 不要只張貼程式碼,需要解說並闡述後續的改進。 :notes: jserv ::: ### q_new 增加新節點 ```c struct list_head *q_new() { struct list_head *q = (struct list_head *) malloc(sizeof(struct list_head)); if (q) { INIT_LIST_HEAD(q); return q; } return NULL; } ``` ### q_free 釋放list使用到的記憶體 原本沒有想到要判斷 l 是否是 NULL ,在 check 時一直 segmentation fault 後來到 qtest 中才發現 quit 也會做ㄧ次 free ```c void q_free(struct list_head *l) { if (!l) return; struct list_head *li = l->next; while (li != l) { struct list_head *temp = li; li = li->next; element_t *e = container_of(temp, element_t, list); q_release_element(e); } free(l); } ``` ### q_insert_head 在 list 前新增節點 ```c bool q_insert_head(struct list_head *head, char *s) { if (!head) return false; element_t *e = (element_t *) malloc(sizeof(element_t)); if (!e) return false; e->value = (char *) malloc(sizeof(char) * (strlen(s) + 1)); if (!e->value) { free(e); return false; } strncpy(e->value, s, strlen(s)); list_add(&e->list, head); e->value[strlen(s)] = '\0'; return true; } ``` ### q_insert_tail 在 list 後新增節點 ```c bool q_insert_tail(struct list_head *head, char *s) { if (!head) return false; element_t *e = (element_t *) malloc(sizeof(element_t)); if (!e) return false; e->value = (char *) malloc(sizeof(char) * (strlen(s) + 1)); if (!e->value) { free(e); return false; } strncpy(e->value, s, strlen(s)); list_add_tail(&e->list, head); e->value[strlen(s)] = '\0'; return true; } ``` ### q_remove_head 在 list 頭刪除節點 好像還有些 bug ,會造成 segmentation fault 目前猜測是我沒仔細看 qtest ```c element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { if (!list_empty(head)) { struct list_head *h = head->next; element_t *e = container_of(h, element_t, list); strncpy(sp, e->value, bufsize - 1); list_del(h); sp[bufsize - 1] = '\0'; return e; } return NULL; } ``` ### q_remove_tail 在 list 尾刪除節點 目前問題如同 q_remove_head ```c element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize) { if (!list_empty(head)) { struct list_head *t = head->prev; element_t *e = container_of(t, element_t, list); strncpy(sp, e->value, bufsize - 1); list_del(t); sp[bufsize - 1] = '\0'; return e; } return NULL; } ``` ### q_size 計算 list 有多少節點 參考自[lab0](https://hackmd.io/@sysprog/2020-lab0) ```c int q_size(struct list_head *head) { if (!head) return 0; int len = 0; struct list_head *li; list_for_each (li, head) len++; return len; } ``` ### q_delete_mid 刪除中間的節點 從頭和尾同時往中間找 ```c bool q_delete_mid(struct list_head *head) { if (!head) return NULL; struct list_head *temp1 = head->next; struct list_head *temp2 = head->prev; while (temp1 != temp2 && temp1->next != temp2) { temp1 = temp1->next; temp2 = temp2->prev; } list_del(temp1); free(temp1); return true; } ``` ### q_delete_dup 刪除重複元素的節點 ```c bool q_delete_dup(struct list_head *head) { if (!head) return NULL; struct list_head *temphead = head->next; while (temphead != head) { bool flag = 0; element_t *tempheadElement = container_of(temphead, element_t, list); struct list_head *temp = temphead->next; while (temp != head) { element_t *tempElement = container_of(temp, element_t, list); if (strcmp(tempElement->value, tempheadElement->value) == 0) { flag = 1; list_del(temp); temp = temp->next; free(tempElement); } else { break; } } temphead = temphead->next; if (flag) { list_del(temphead->prev); free(tempheadElement); } } return true; } ``` :::warning 試著縮減上述程式碼,撰寫更精簡有效的實作 :notes: jserv ::: ### q_swap 交換相鄰的節點 ```c void q_swap(struct list_head *head) { // https://leetcode.com/problems/swap-nodes-in-pairs/ if (!head || list_empty(head) || list_is_singular(head)) return; else { struct list_head *temp1, *temp2, *temp3; temp1 = head->next; temp2 = temp1->next; temp3 = head; while (1) { temp1->next = temp2->next; temp1->prev = temp2; temp2->next = temp1; temp2->prev = temp3; temp3->next = temp2; if (temp1->next != head && temp1->next->next != head) { temp3 = temp1; temp1 = temp1->next; temp2 = temp1->next; } else { break; } } } } ``` ### q_reverse 翻轉整個 list ```c void swap_two_node(struct list_head **node1, struct list_head **node2) { struct list_head *temp; temp = *node1; *node1 = *node2; *node2 = temp; } void q_reverse(struct list_head *head) { struct list_head *temp = head; swap_two_node(&temp->next, &temp->prev); list_for_each (temp, head) { swap_two_node(&temp->next, &temp->prev); } } ``` ### q_sort 按升序排列 參考[你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/YA7RMqd6RE2UL8OmmjU9PQ) 雖然寫過mergesort,不過第一次對 linked list 做排序,一直寫錯 ```c struct list_head *merge(struct list_head *l, struct list_head *r) { struct list_head *head = NULL; struct list_head **indirect = &head; while (l && r) { element_t *elementl = container_of(l, element_t, list); element_t *elementr = container_of(r, element_t, list); if (strcmp(elementl->value, elementr->value) < 0) { *indirect = l; l = l->next; } else { *indirect = r; r = r->next; } indirect = &(*indirect)->next; } *indirect = (struct list_head *) ((uintptr_t) l | (uintptr_t) r); return head; } struct list_head *mergesort(struct list_head *l, struct list_head *r) { struct list_head *tortoise = l; struct list_head *hare = l; if (l == r) { l->next = NULL; return l; } else if (l->next == r) { element_t *left = container_of(l, element_t, list); element_t *right = container_of(r, element_t, list); if (strcmp(left->value, right->value) > 0) { l->next = NULL; r->next = l; return r; } else { r->next = NULL; return l; } } while (hare->next != NULL && hare->next->next != NULL && hare->next != r && hare->next->next != r) { hare = hare->next->next; tortoise = tortoise->next; } struct list_head *list1, *list2; struct list_head *temp = tortoise->next; list1 = mergesort(l, tortoise); list2 = mergesort(temp, r); return merge(list1, list2); } void q_sort(struct list_head *head) { if (!head || list_empty(head) || list_is_singular(head)) return; head->prev->next = NULL; head->next = mergesort(head->next, head->prev); struct list_head *node = head; while (node->next) { node->next->prev = node; node = node->next; } node->next = head; head->prev = node; } ``` 目前完成了 queue.c 的部份,不過應該還會修改,大部分的程式碼都是沒想清楚就寫的,應該可以再簡潔一些