# 2020q1 Homework (lab0) contributed by <`love1357983`> ###### tags: `linux2020` ## :penguin: 作業描述 [lab01](https://hackmd.io/@sysprog/linux2020-lab0#-作業要求) ## 開發紀錄 ### queue_t #### 說明: 為了讓 `q_insert_tail` 和 `q_size` 在 $O(1)$ 的時間複雜度, 在 `queue_t` 結構中增加以下欄位。 * `tail` 變數名稱 - `list_ele_t` 型態 * `size` 變數名稱 - `int` 型態 #### `queue.h` 實作: ```cpp typedef struct { list_ele_t *head; list_ele_t *tail; int size; } queue_t; ``` ### 建立 #### 說明: 在執行 `qtest` 程式一開始,會先呼叫 `queue.h` 中 `q_new` 函式,將欄位進行初始化。其中使用 `malloc` 函式,配置一個 `queue_t` 需要的空間,如果成功的話會回傳 `void*` 的記憶體位址;若失敗則回傳 `NULL` ,所以需要檢查 `malloc` 是否成功。 #### ***q_new*** ```cpp queue_t *q_new(void) { queue_t *q = malloc(sizeof(queue_t)); if (!q) return q; q->head = q->tail = NULL; q->size = 0; return q; } ``` ### 釋放 #### 說明: 將所建立 `queue_t` 型態的 `q` 記憶體空間釋放,包含每個節點上的動態配置的資料 `value` 欄位。在運行前一樣會先檢查 `queue_t` 是否存在,如果不存在直接返回。在這裡使用 `while` 迴圈走訪鏈結串列中各個節點。在 `while` 迴圈中,使用 `prev` 記錄目前 `tmp` 的記憶體位置,在 `tmp` 移動到下一個節點後,再將該節點中的 `next` 欄位作釋放動作,完成該節點在記憶裡空間完全釋放完畢。 #### ***q_free*** ```cpp void q_free(queue_t *q) { if (!q) return; list_ele_t *tmp = q->head; while (tmp) { list_ele_t *ptr = tmp; free((tmp->value)); tmp = tmp->next; free(ptr); } free(q); } ``` ### `LIFO`插入 #### 說明: 使用 `LIFO` 方法進行排序,先建立 `newh` 的 `list_ele_t` 節點,將接收到的 `s` ,使用 `strdup` 複製字串加入 `newh` 的 `value` 中。把 `q->head` 接續在 `newh` 的 `next` 中,修改 `q->head` 為 `newh` ,判斷 `q_size` 是否為 `0` ,如果為 `0` 時,再把 `q->tail` 設定為 `newh` ,最後再將 `q_size` 增加 `1`。 #### ***q_insert_head*** ```cpp bool q_insert_head(queue_t *q, char *s) { if (!q) return false; list_ele_t *newh = malloc(sizeof(list_ele_t)); if (!newh) { free(newh); return false; } newh->value = strdup(s); if (!newh->value) { free(newh); return false; } newh->next = q->head; q->head = newh; if (!q_size(q)) q->tail = newh; ++q->size; return true; } ``` ### `FIFO`插入 #### 說明: 同理,使用 `FIFO` 方法進行排序,先建立 `newt` 的 `list_ele_t` 節點,同樣將接收到的 `s` ,使用 `strdup` 複製字串加入 `newt` 的 `value` 中,把 `newt` 的 `next` 設定為 `NULL` ,把原 `q->tail->next` 為 `NULL` 設定為先前建立出的 `newt` ,再把原先的 `q->tail` 設定為 `newt` 進行取代,最後將 `q_size` 增加 `1` 。另外,在呼叫此函式開始時,會先取得 `q_size` 進行判斷,如果為 `0` 時,會呼叫與 `q_insert_head` 相同的程式碼,進行新增節點動作。 #### ***q_insert_tail*** ```c= bool q_insert_tail(queue_t *q, char *s) { if (!q) return false; if (!q_size(q)) { q_insert_head(q, s); return true; } list_ele_t *newt = malloc(sizeof(list_ele_t)); if (!newt) { free(newt); return false; } newt->value = strdup(s); if (!newt->value) { free(newt); return false; } newt->next = NULL; q->tail->next = newt; q->tail = newt; ++q->size; return true; } ``` ### 刪除 #### 說明: 將取得 `q->head->value` 節點內容複製到 `sp` 中,然後釋放該節點及資料所佔用的記憶體空間。先建立 `tmp` 的 `list_ele_t` 節點,記錄 `q->head` 該節點記憶體位置,再將 `q->head` 指向 `tmp->next` 記憶體位置,再把 `tmp` 記憶體位置進行釋放,將 `q_size` 減 `1` 。若 `q_size` 等於 `0` 時,須將 `q->tail` 記憶體空間進行清空動作。 #### ***q_remove_head*** ```c= bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (!q || !q->head) return false; if (bufsize > 0 && sp) { strncpy(sp, q->head->value, bufsize - 1); sp[bufsize - 1] = '\0'; } list_ele_t *tmp = q->head; q->head = tmp->next; --q->size; if (!q_size(q)) q->tail = NULL; free(tmp->value); free(tmp); return true; } ``` ### 個數 #### 說明: 如 `queue_t` 加入記錄佇列的 `size` 個數,可將計算 linked list 減少所需時間,將時間複雜度從 $O(n)$ 縮短到 $O(1)$ 。 #### ***q_size*** ```c= int q_size(queue_t *q) { return (q == NULL) ? 0 : q->size; } ``` ### 反轉 #### 說明: 此函式限制在不建立額外配置空間下,先建立 `prev` 初始狀態為 `NULL`, `curr` 初始狀態為 `q->head`, `prec` 初始狀態為 `q->head->next` ,共三個 `list_ele_t` 節點。將當前的 `curr` 佇列位置從新指向 `next` 的 `prev` 記憶體位置,因為 `curr` 的 `next` 已經從新指向了,所以 `prec` 節點是為了讀取原有 `q->next` 佇列中的記憶體位置,使用 `while` 迴圈進行 `queue_t` 輪詢至 `prec` 為 `NULL` 為止,最後在將 `queue_t` 中 `head`, `tail` 進行對調動作。 #### ***q_reverse*** ```cpp void q_reverse(queue_t *q) { if (!q || !q->head) return; list_ele_t *prev = NULL, *curr = q->head, *prec = q->head->next; while (prec) { curr->next = prev; prev = curr; curr = prec; prec = prec->next; } curr->next = prev; q->tail = q->head; q->head = curr; return; } ``` ### 排序 #### 說明: 以下程式碼使用 [Bubble Sort](https://en.wikipedia.org/wiki/Bubble_sort) 法,以==遞增順序==來排序鏈結串列的元素,其執行的時間複雜度為 $O(n^2)$ 。透過 `strcasecmp` 將 `first->value` 和 `curr->value` 比較,當`strcasecmp` 回傳數值小於 `0` 時,把兩個節點的 `value` 交換動作。 :::warning :bookmark_tabs: 這邊程式碼可能比較偷懶,只對 `list_ele_t` 中的 `value` 內容進行交換的動作,這邊有可能在兩者被分配的記憶體空間大小不同,而造成記憶體洩露 (memory leak),後續會針對樣的問題進行實驗與研究。 ::: #### ***q_sort*** ```cpp void q_sort(queue_t *q) { if (!q || !q->head) return; list_ele_t *first = q->head; while (first) { list_ele_t *curr = q->head; while (curr) { if (strcasecmp(first->value, curr->value) < 0) { char *tmp = curr->value; curr->value = first->value; first->value = tmp; } curr = curr->next; } first = first->next; } return; } ``` ## make test評分 // Mar 3 -> make test ``` --- Trace Points +++ TESTING trace trace-01-ops: # Test of insert_head and remove_head --- trace-01-ops 6/6 +++ TESTING trace trace-02-ops: # Test of insert_head, insert_tail, and remove_head --- trace-02-ops 6/6 +++ TESTING trace trace-03-ops: # Test of insert_head, insert_tail, reverse, and remove_head --- trace-03-ops 6/6 +++ TESTING trace trace-04-ops: # Test of insert_head, insert_tail, size, and sort --- trace-04-ops 6/6 +++ TESTING trace trace-05-ops: # Test of insert_head, insert_tail, remove_head, reverse, size, and sort # bear gerbil jiu kuo sheng --- trace-05-ops 5/5 +++ TESTING trace trace-06-string: # Test of truncated strings --- trace-06-string 6/6 +++ TESTING trace trace-07-robust: # Test operations on NULL queue --- trace-07-robust 6/6 +++ TESTING trace trace-08-robust: # Test operations on empty queue --- trace-08-robust 6/6 +++ TESTING trace trace-09-robust: # Test remove_head with NULL argument --- trace-09-robust 6/6 +++ TESTING trace trace-10-malloc: # Test of malloc failure on new --- trace-10-malloc 6/6 +++ TESTING trace trace-11-malloc: # Test of malloc failure on insert_head --- trace-11-malloc 6/6 +++ TESTING trace trace-12-malloc: # Test of malloc failure on insert_tail --- trace-12-malloc 6/6 +++ TESTING trace trace-13-perf: # Test performance of insert_tail --- trace-13-perf 6/6 +++ TESTING trace trace-14-perf: # Test performance of size --- trace-14-perf 6/6 +++ TESTING trace trace-15-perf: # Test performance of insert_tail, size, reverse, and sort ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient ERROR: Not sorted in ascending order --- trace-15-perf 0/6 +++ TESTING trace trace-16-perf: # Test performance of sort with random and descending orders # 10000: all correct sorting algorithms are expected pass # 50000: sorting algorithms with O(n^2) time complexity are expected failed # 100000: sorting algorithms with O(nlogn) time complexity are expected pass ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient ERROR: Not sorted in ascending order ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient ERROR: Not sorted in ascending order ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient ERROR: Not sorted in ascending order ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient ERROR: Not sorted in ascending order --- trace-16-perf 0/6 +++ TESTING trace trace-17-complexity: # Test if q_insert_tail and q_size is constant time complexity Probably constant time Probably constant time --- trace-17-complexity 5/5 --- TOTAL 88/100 ```