# 2023q1 Homework1 (lab0)
contributed by < `PlusThousand0107` >
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 12
On-line CPU(s) list: 0-11
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
CPU family: 6
Model: 158
Thread(s) per core: 2
Core(s) per socket: 6
Socket(s): 1
Stepping: 10
CPU max MHz: 4100.0000
CPU min MHz: 800.0000
BogoMIPS: 4399.99
```
## 開發過程
:::danger
開發紀錄著重於你的想法、觀察、中間遇到的難題和你如何克服,程式碼應是輔助性質,列出最關鍵的部分即可。
:notes: jserv
:::
### q_new
目標: 建立一個空佇列
:::danger
注意書寫規範:中英文間用一個半形空白字元區隔。
:notes: jserv
:::
流程:
1. 先用 `malloc` 對宣告一個大小為 `list_head` 的記憶體空間
2. 再利用 `INIT_LIST_HEAD()` 做初始化
### q_free
目標: 釋放佇列佔用的記憶體
流程:
1. 先確認傳入的 `list_head *l` 是否為 NULL,如果是的話就直接結束
2. 利用 `list_for_each_entry_safe` 進行走訪,每過一個節點就用 `list_del` 做移除並釋放該節點空間,程式碼如下所示
```c
list_for_each_entry_safe (cur, n, l, list) {
list_del(&cur->list);
q_release_element(cur);
}
```
3. 都走訪完成最後才釋放 `*l` 的記憶體空間
### q_size
目標: 計算佇列中的節點數量
流程:
1. 先確認傳入的 `list_head *q_head` 是否為 NULL,如果是就直接回傳0
2. 宣告變數 `len` 紀錄當前節點數量,並用 `list_for_each` 進行走訪,每過一個節點就遞增 `len`
3. 都走訪完後會回傳 `len`
### q_insert_head
目標: 在佇列開頭插入新節點
流程:
1. 先用 `malloc` 對宣告一個大小為 `element_t` 的記憶體空間,並將位址傳給宣告的指標變數 `node`
2. 用 `strdup` 將字串複製給 `node->value`
3. 用 `list_add` 將節點加在 `head` 的後面一位,詳細程式碼如下所示
```c
element_t *node = malloc(sizeof(*node));
node->value = strdup(s);
list_add(&node->list, head);
```
### q_insert_tail
目標: 在佇列結尾處插入新節點
流程:
1. 先用 `malloc` 對宣告一個大小為 `element_t` 的記憶體空間,並將位址傳給宣告的指標變數 `node`
2. 用 `strdup` 將字串複製給 `node->value`
3. 用 `list_add_tail` 將節點加在佇列最尾端,除了將 `list_add` 改成了 `list_add_tail` 外,其餘均與 `q_insert_head` 相同
### q_swap
目標: 將佇列中鄰近的節點兩兩交換
流程:
1. 宣告一個指標變數 `a` ,並指向 `head->next` ,再宣告一個指標變數 `b` ,使其指向 `head->next->next`
2. 只要 `a` 和 `b` 沒有指回到 `head` ,就會修改它們指的對象,將原先指向 `a` 的改成指向 `b`,指向 `b` 的改成指向 `a` ,詳細程式碼如下所示
```c
while (a != head && b != head) {
a->prev->next = b;
a->next = b->next;
b->prev = a->prev;
a->prev = b;
b->next->prev = a;
b->next = a;
a = a->next;
b = a->next;
}
```
### q_reverse
目標: 將鏈結串列以反向順序重新排列
流程:
1. 宣告一個指標變數 `a` ,並指向 `head` ,再宣告一個指標變數 `b` ,使其指向 `head->next`
2. 主要操作以修改 `a` 的指標為主,將原本指向 `next` 的改成指向 `prev` ,將原本指向 `prev` 的改成指向 `next` ,以此方法將順序反轉,直到 `a` 又回到 `head` 為止,詳細程式碼如下所示
```c
do {
a->next = a->prev;
a->prev = b;
a = b;
b = a->next;
} while (a != head);
```
### q_remove_head
目標: 將佇列開頭的節點移除 (remove)
流程:
1. 要移除的第一個元素為 `head->next`
2. 移除後須將 `head->next->value` 複製到 `buffer` 中,詳細程式碼如下所示
```c
element_t *node = list_entry(head->next, element_t, list);
list_del(head->next);
strncpy(sp, node->value, bufsize - 1);
sp[bufsize - 1] = '\0';
```
### q_remove_tail
目標: 將佇列結尾的節點移除 (remove)
流程:
1. 要移除的第一個元素為 `head->prev`
2. 移除後須將 `head->prev->value` 複製到 `buffer` 中,除了將 `head->next` 改成了 `head->prev` 外,其餘均與 `q_remove_head` 相同
### q_delete_mid
目標: 將佇列中間的節點刪除 (delete)
流程:
1. 宣告一個指標變數 `a` ,並指向 `head->next` ,再宣告一個指標變數 `b` ,使其指向 `head->prev`
2. 讓 `a` 往前走,再讓 `b` 往後走,直到 `a != b` 或者 `a->next != b` ,表示已找出中間節點,程式碼如下所示
```c
while ((a != b) && (a->next != b)) {
a = a->next;
b = b->prev;
}
```
3. 刪除中間的節點並釋放其記憶體空間
### q_reverseK
目標: 將鏈結串列以每 k 個節點為一組,進行反向順序重新排列
流程:
1. 先確認傳入的 `list_head *head` 不為 NULL 且 k 不能小於 1 ,如果有一個不符就直接結束
2. 宣告變數 `cnt` 紀錄每組節點數量,用 `list_for_each_safe` 進行走訪,每過一個節點就遞增 `cnt`
3. 當 `cnt` 等於 k 時,會用 `list_cut_position` 進行切斷,並用上面實作出的 `q_reverse` 反轉順序,最後再用 `list_splice_init` 接合回原本的鏈結串列,詳細程式碼如下所示,這裡參考到了 [komark06](https://hackmd.io/@komark06/SyCUIEYpj#q_reverseK-%E5%AF%A6%E4%BD%9C) 同學的程式碼
```c
list_for_each_safe (cur, safe, head) {
cnt++;
if (cnt == k) {
list_cut_position(&h, temp, cur);
q_reverse(&h);
list_splice_init(&h, temp);
cnt = 0;
temp = safe->prev;
}
}
```
### q_delete_dup
目標: 再**已排序**的狀況下,刪除佇列中內容重複的節點
流程:
1. 先確認傳入的 `list_head *head` 是否為 NULL,如果是就直接回傳 false
2. 用 `list_for_each_entry_safe` 進行走訪,並用 `strcmp` 判斷元素的值是否相同,詳細程式碼如下所示,這裡參考到了 [yanjiew1](https://hackmd.io/@yanjiew/linux2023q1-lab0#q_delete_dup) 同學的程式碼
```c
list_for_each_entry_safe (it, safe, head, list) {
if (&safe->list != head && strcmp(safe->value, it->value) == 0)
continue;
if (it->list.prev != cut) {
LIST_HEAD(temp);
list_cut_position(&temp, cut, &it->list);
list_splice(&temp, &trash);
}
cut = safe->list.prev;
}
```
3. 最後會用 `list_for_each_entry_safe` 走訪蒐集到的重複元素,並用 `q_release_element` 進行刪除