--- tags: linux2020 --- # 2020q3 Homework1 (lab0) contributed by < `kevin30292` > code [GitHub](https://github.com/kevin30292/lab0-c) ## function 功能 queue.c 僅提供介面但尚未有完整程式實作,需要補完並逐步精進,包含以下: 1. q_new: 建立新的「空」佇列; 2. q_free: 釋放佇列所佔用的記憶體; 3. q_insert_head: 在佇列開頭 (head) 加入 (insert) 給定的新元素 (以 LIFO 準則); 4. q_insert_tail: 在佇列尾端 (tail) 加入 (insert) 給定的新元素 (以 FIFO 準則); 5. q_remove_head: 在佇列開頭 (head) 移除 (remove) 給定的元素。 6. q_size: 計算佇列中的元素數量。 7. q_reverse: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列元素,換言之,它只能重排已經存在的元素; 8. q_sort: 「進階電腦系統理論與實作」課程所新增,以遞增順序來排序鏈結串列的元素,可參閱 Linked List Sort 得知實作手法 ## 開發紀錄 ### `q_size` 由於要在 O(1) 的常數時間內執行完成,所以也不可能走訪整個鏈結串列,以取得佇列的容積。 新增 size 值紀錄 queue 的大小,又提示 >Return 0 if q is NULL or empty ```clike= int q_size(queue_t *q) { return (q == NULL) ? 0 q->size } ``` ### `new_q` >TODO: What if malloc returned NULL? `malloc` 回傳 NULL 可能為記憶體不足,因此失敗。 加入檢查機制: ```c= if(q == NULL){ printf("Error! Allocation was failed. \n"); return 1; } ``` ### `q_free` 需逐一 `free` 所有queue中的值及字串 ```c= void q_free(queue_t *q) { if (q != NULL) { list_ele_t *next; while (q->head != NULL) { next = q->head->next; free(q->head->value); free(q->head); q->head = next; } free(q); } } ``` ### `q_insert_head` 第19行需要 `free(newh)` 因為第10行的 `malloc` 失敗,但第6行的malloc是有成功的 ```c= bool q_insert_head(queue_t *q, char *s) { list_ele_t *newh; if (q != NULL) { list_ele_t *newh; newh = malloc(sizeof(list_ele_t)); if (newh != NULL) { int length; length = strlen(s); newh->value = malloc(length * sizeof(char)); if (newh->value != NULL) { memcpy(newh->value, s, length); newh->next = q->head; q->head = newh; q->size++; return true; } else { free(newh); return false } } else { return false; } } else { return false; } } ``` ### `q_insert_tail` * 要在 O(1) 的常數時間內執行完成 >不能從 `head` 慢慢找 tail > 加入 `tail` 性質 `queue.h` 加入: ```c= list_ele_t *tail; ``` 需修改 code 以維護此參數 `new_q` 加入: ```c= q->tail = NULL; // Add for the q_insert_tail ``` `q_insert_head` 加入: ```c= if (q->size == 0) { q->tail = newh; } ``` 12-16行,避免 `head` 缺失情況 ```c= bool q_insert_tail(queue_t *q, char *s) { if (q != NULL) { list_ele_t *newt; newt = malloc(sizeof(list_ele_t)); if (newt != NULL) { int length; length = strlen(s) + 1; newt->value = malloc(length * sizeof(char)); if (newt->value != NULL) { memcpy(newt->value, s, length); if (q->size == 0) { q->head = newt; } else { q->tail->next = newt; } q->tail = newt; newt->next = NULL; q->size++; return true; } else { free(newt); return false; } } else { return false; } } else { return false; } } ``` ### Debug 測試時發現==亂碼==及 ==Segmentation fault== ```shell q = [gerbil▒▒▒ bear▒▒▒ dolphin▒▒▒] cmd> # Now at the tail cmd> it meerkat Segmentation fault occurred. You dereferenced a NULL or invalid pointer make: *** [Makefile:51: check] Aborted (core dumped) ``` Segmentation fault 部分,檢查發現 `new_q` 沒有: 1. initialize `q->size` 2. 檢查 `q` 是否為 NULL 改寫 `q_new` ```c= queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); if (q == NULL) { printf("Error! Allocation was failed. \n"); return q; } else { q->head = NULL; q->tail = NULL; // Add for the q_insert_tail q->size = 0; return q; } } ``` 亂碼部分,檢查發現 `q_insert_head` length 值錯誤 ```c= length = strlen(s); ``` 改為: ```c= length = strlen(s) + 1; ``` ### `q_reverse` 用兩個 pointer 紀錄,過程中的 `head` 及 `tail` 命名為 `tmph` 及 `tmpt` 從舊 `head` 開始反轉,`head->next` 指回 `head` 以此類推。 維持 `q->head` 指向尚未反轉的佇列的首項 ```c= void q_reverse(queue_t *q) { if (q != NULL && q->size > 1) { list_ele_t *tmph, *tmpt; tmpt = q->head; tmph = q->head->next; q->tail = q->head; while (q->head->next != NULL) { q->head = tmph->next; tmph->next = tmpt; tmpt = tmph; tmph = q->head; } q->head->next = tmpt; q->tail->next = NULL; } } ``` 圖解: 1. 初始 ==tmpt== 及 ==tmph== ```graphviz digraph linkedlist { rankdir=LR node [shape=record] a [label="{ <data> 1 | <ref> }", width=1] b [label="{ <data> 2 | <ref> }", width=1] c [label="{ <data> 3 | <ref> }", width=1] head [shape=box, width=0.5, color=blue] tail [shape=box, width=0.5, color=black] tmph [shape=box, width=0.5, color=red] tmpt [shape=box, width=0.5, color=red] a:ref:a -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2] b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false] tail -> c head -> a tmpt -> a tmph -> b } ``` 2. `tail` 移至 `head` ```graphviz digraph linkedlist { rankdir=LR node [shape=record] a [label="{ <data> 1 | <ref> }", width=1] b [label="{ <data> 2 | <ref> }", width=1] c [label="{ <data> 3 | <ref> }", width=1] head [shape=box, width=0.5, color=blue] tail [shape=box, width=0.5, color=black] tmph [shape=box, width=0.5, color=red] tmpt [shape=box, width=0.5, color=red] a:ref:a -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2] b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false] tail -> a head -> a tmpt -> a tmph -> b } ``` 3. while 迴圈 ```graphviz digraph linkedlist { rankdir=LR node [shape=record] a [label="{ <data> 1 | <ref> }", width=1] b [label="{ <data> 2 | <ref> }", width=1] c [label="{ <data> 3 | <ref> }", width=1] head [shape=box, width=0.5, color=blue] tail [shape=box, width=0.5, color=black] tmph [shape=box, width=0.5, color=red] tmpt [shape=box, width=0.5, color=red] a:ref:a -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2] b:ref:c -> a:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false] tail -> a head -> c tmpt -> a tmph -> b } ``` ```graphviz digraph linkedlist { rankdir=LR node [shape=record] a [label="{ <data> 1 | <ref> }", width=1] b [label="{ <data> 2 | <ref> }", width=1] c [label="{ <data> 3 | <ref> }", width=1] head [shape=box, width=0.5, color=blue] tail [shape=box, width=0.5, color=black] tmph [shape=box, width=0.5, color=red] tmpt [shape=box, width=0.5, color=red] a:ref:a -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2] b:ref:c -> a:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false] tail -> a head -> c tmpt -> b tmph -> c } ``` 4. while 迴圈結束 `head->next` 為 ==NULL== ```graphviz digraph linkedlist { rankdir=LR node [shape=record] a [label="{ <data> 1 | <ref> }", width=1] b [label="{ <data> 2 | <ref> }", width=1] c [label="{ <data> 3 | <ref> }", width=1] head [shape=box, width=0.5, color=blue] tail [shape=box, width=0.5, color=black] tmph [shape=box, width=0.5, color=red] tmpt [shape=box, width=0.5, color=red] b:ref:c -> a:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false] c:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false] tail -> a head -> c tmpt -> b tmph -> c } ``` ### `q_sort` ```c= queue_t* merge_list(queue_t *l1, queue_t *l2) { if (!l1) return l2; if (!l2) return l1; if( l1->value < l2->value) { l1->next = merge(l1->next, l2); return l1; } else { l2->next = merge(l2->next, l1); return l2; } } queue_t* merge_sort(list_ele_t *l) { if (!q || !q->next) return q; list_ele_t* fast; list_ele_t* slow; fast = q->head->next; slow = q->head; while (fast && fast->next) { fast = fast->next->next; slow = slow->next; } fast = slow->next; slow->next = NULL; list_ele_t* l1 = merge_sort(q); list_ele_t* l2 = merge_sort(q2); return merge_list(l1,l2); } void q_sort(queue_t *q) { if (q && q->size > 0) { merge_sort(q); return; } else { printf("Something going wrong!!"); return; } } ``` 卡在merge_sort(),不知道應該用什麼型態的 input ,考慮到 recursive 呼叫。 參考前人實作:[ZhuMon](https://hackmd.io/@ZhuMon/lab0-c),採用 pointer to pointer 的方式,即可不用考慮回傳 type 的問題。 ```c= void merge_sort(list_ele_t **head) { if(!(*head) || !((*head)->next)) return; list_ele_t* fast; list_ele_t* slow; fast = (*head)->next; slow = *head; while (fast && fast->next) { fast = fast->next->next; slow = slow->next; } fast = slow->next; slow->next = NULL; slow = *head; merge_sort(&fast); merge_sort((&slow)); list_ele_t **tmp = head; *head = NULL; while (fast && slow) { if (strcmp(fast->value, slow->value) < 0) { *tmp = fast; fast = fast->next; } else { *tmp = slow; slow = slow->next; } tmp = &(*tmp)->next; } *tmp = fast ? fast : slow; } void q_sort(queue_t *q) { if (!q || q->size == 1) { return; } else if (q && q->size > 1) { merge_sort(&q->head); return; } else { return; } } ``` 發現 sorting 在包含大寫的時候會不正常: