# 2023q1 Homework1 (lab0)
contributed by < `oEric0929o` >
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.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): 12
On-line CPU(s) list: 0-11
Thread(s) per core: 2
Core(s) per socket: 6
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 167
Model name: 11th Gen Intel(R) Core(TM) i5-11400F @ 2.60GHz
Stepping: 1
CPU MHz: 2600.000
CPU max MHz: 4400.0000
CPU min MHz: 800.0000
BogoMIPS: 5184.00
```
## 開發過程
拿到這份作業的一開始可以說是毫無頭緒,自己嘗試閱讀資料後不知道要如何開始,直到詢問 [Thegoatistasty](https://hackmd.io/@pigwei/r1jWYNipi) 同學,他建議我依照測資順序一步一步完成,才慢慢開始有進度。撰寫程式的過程大多是先想辦法讀懂同學的程式碼,再自己寫出來,過程中有明顯感覺到自己有進步,最後有幾個函式靠自己查資料完成,很有成就感。能完成這份作業真的很感謝 [Thegoatistasty](https://hackmd.io/@pigwei/r1jWYNipi) 同學的幫忙,給了我方向也解答了我很多疑問。
### q_new
```c
/* Create an empty queue */
struct list_head *q_new()
{
struct list_head *p = malloc(sizeof(struct list_head));
if (!p)
return NULL;
INIT_LIST_HEAD(p);
return p;
}
```
### q_free
```c
/* Free all storage used by queue */
void q_free(struct list_head *l)
{
if (!l)
return;
element_t *temp, *safe;
list_for_each_entry_safe (temp, safe, l, list) {
list_del(&temp->list);
free(temp->value);
free(temp);
}
free(l);
}
```
### q_insert_head
原本用 ``` strcpy ``` ,執行時報錯,參考[文件](https://security.web.cern.ch/recommendations/en/codetools/c.shtml)後改用 ```snprintf```
```c
/* Insert an element at head of queue */
bool q_insert_head(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *new_node = (element_t *) malloc(sizeof(element_t));
if (!new_node)
return false;
new_node->value = (char *) malloc(strlen(s) + 1);
if (!new_node->value) {
free(new_node);
return false;
}
snprintf(new_node->value, strlen(s) + 1, "%s", s);
list_add(&new_node->list, head);
return true;
}
```
### q_insert_tail
將 ```q_insert_head``` 中的 ``` list_add ``` 改成 ```list_add_tail``` 即可
```c
/* Insert an element at tail of queue */
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *new_node = (element_t *) malloc(sizeof(element_t));
if (!new_node)
return false;
new_node->value = (char *) malloc(strlen(s) + 1);
if (!new_node->value) {
free(new_node);
return false;
}
snprintf(new_node->value, strlen(s) + 1, "%s", s);
list_add_tail(&new_node->list, head);
return true;
}
```
### q_remove_head
一開始想了很久想不出 ```sp``` 的功能,仔細看了 ```queue.h``` 中的敘述才發現是用來儲存刪除節點的字串內容
```c
/* Remove an element from head of queue */
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head) || !sp)
return NULL;
element_t *p = list_entry(head->next, element_t, list);
strncpy(sp, p->value, bufsize - 1);
sp[bufsize - 1] = '\0';
list_del(&p->list);
return p;
}
```
### q_remove_tail
```q_remove_head``` 刪除 ```head``` 的下一個節點,而因為是雙向鏈結串列,所以 ```q_remove_tail``` 只要改成刪除 ```head``` 的前一個節點即可
```c
/* Remove an element from tail of queue */
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head) || !sp)
return NULL;
element_t *p = list_entry(head->prev, element_t, list);
strncpy(sp, p->value, bufsize - 1);
sp[bufsize - 1] = '\0';
list_del(&p->list);
return p;
}
```
### q_size
```c
/* Return number of elements in queue */
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
/* Delete the middle node in queue */
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || list_empty(head))
return false;
struct list_head *fast, *slow;
fast = head->next->next;
slow = head->next;
while (fast != head && fast->next != head) {
fast = fast->next->next;
slow = slow->next;
}
list_del(slow);
element_t *del = list_entry(slow, element_t, list);
free(del->value);
free(del);
return true;
}
```
### q_delete_dup
參考 [GeeksforGeeks](https://www.geeksforgeeks.org/remove-duplicates-from-a-sorted-linked-list/) 的作法刪除多餘重複節點,再刪除每組重複節點的第一個節點
```c
/* Delete all nodes that have duplicate string */
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head || list_empty(head))
return false;
struct list_head *current = head->next;
element_t *cur_node, *next_node;
bool dup = false;
while (current->next != head) {
cur_node = list_entry(current, element_t, list);
next_node = list_entry(current->next, element_t, list);
if (strcmp(cur_node->value, next_node->value) == 0) {
dup = true;
list_del(&next_node->list);
q_release_element(next_node);
} else {
current = current->next;
if (dup == true) {
list_del(&cur_node->list);
q_release_element(cur_node);
dup = false;
}
}
}
if (dup) {
list_del(&cur_node->list);
q_release_element(cur_node);
}
return true;
}
```
### q_swap
利用 ```list_move``` 進行交換,將 ```p``` 放到 ```p->next``` 後方之後 ```p->next``` 剛好會是下一個要交換的節點
```c
/* Swap every two adjacent nodes */
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head)
return;
struct list_head *p;
for (p = head->next; p->next != head && p != head; p = p->next)
list_move(p, p->next);
}
```
### q_reverse
利用 ```list_move``` 依序將每個節點放到最前面,即完成反轉
```c
/* Reverse elements in queue */
void q_reverse(struct list_head *head)
{
if (!head)
return;
struct list_head *node, *safe;
list_for_each_safe (node, safe, head)
list_move(node, head);
}
```
### q_reverseK
利用 ```count``` 計數,當 ```count``` 到達 ```k``` 時用 `list_move` 做反轉,並移動 `temp` 來記錄下一次反轉的起始點
```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 || list_empty(head) || k <= 1)
return;
struct list_head *node, *safe, *temp = head;
int count = 0;
list_for_each_safe (node, safe, head) {
count++;
if (count == k) {
struct list_head *cur_node = temp->next,
*next_node = temp->next->next;
while (count != 0) {
list_move(cur_node, temp);
cur_node = next_node;
next_node = next_node->next;
count--;
}
temp = safe->prev;
}
}
}
```
### q_sort
採用 merge sort ,參考[你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list)中雙指標的方法實作 `merge_two_lists` ,再以遞迴的方式實作 merge sort ,排序前將頭尾切開,完成排序後再接起來
```c
/* Merge two sorted lists */
struct list_head *merge_two_lists(struct list_head *l1,
struct list_head *l2,
bool descend)
{
struct list_head *head = NULL;
struct list_head **ptr = &head;
for (; l1 && l2; ptr = &(*ptr)->next) {
element_t *a = list_entry(l1, element_t, list);
element_t *b = list_entry(l2, element_t, list);
if ((strcmp(a->value, b->value) <= 0 && !descend) ||
(strcmp(a->value, b->value) > 0 && descend)) {
*ptr = l1;
l1 = l1->next;
} else {
*ptr = l2;
l2 = l2->next;
}
}
*ptr = (struct list_head *) ((uintptr_t) l1 | (uintptr_t) l2);
return head;
}
/* Merge sort recursively */
struct list_head *merge_sort(struct list_head *head, bool descend)
{
if (!head || !head->next)
return head;
struct list_head *fast, *slow;
fast = slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
slow->prev->next = NULL;
struct list_head *l1 = merge_sort(head, descend);
struct list_head *l2 = merge_sort(slow, descend);
return merge_two_lists(l1, l2, descend);
}
/* Sort elements of queue in ascending/descending order */
void q_sort(struct list_head *head, bool descend)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
head->prev->next = NULL;
head->next = merge_sort(head->next, descend);
head->next->prev = head;
struct list_head *p = head;
while (p->next) {
p->next->prev = p;
p = p->next;
}
head->prev = p;
p->next = head;
}
```
### q_descend
以反序走訪,遇到較小的節點就刪除。架構和 `q_delete_dup` 很相似,將刪除的條件判斷從相等改為小於
```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 || list_empty(head))
return 0;
struct list_head *current = head->prev;
element_t *cur_node = NULL, *prev_node = NULL;
int len = 1;
while (current->prev != head) {
cur_node = list_entry(current, element_t, list);
prev_node = list_entry(current->prev, element_t, list);
if (strcmp(cur_node->value, prev_node->value) < 0) {
list_del(&prev_node->list);
q_release_element(prev_node);
} else {
len++;
current = current->prev;
}
}
return len;
}
```
### q_merge
一開始對 `queue_contex_t` 和 `list_head` 不夠了解,一直想不明白 `next` 代表的意義,查看 `queue.h` 才發現這裡的參數 `head` 和其他函式不同,是 header of chain ,所以 `head->next` 是會在 chain 之間移動而不是 list 。將 chain 中的每一條 list 接在一起再做排序即可完成 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 || list_empty(head))
return 0;
queue_contex_t *p = list_entry(head->next, queue_contex_t, chain);
if (list_is_singular(head))
return p->size;
struct list_head *node;
for (node = head->next->next; node != head; node = node->next) {
queue_contex_t *tmp = list_entry(node, queue_contex_t, chain);
list_splice_init(tmp->q, p->q);
}
q_sort(p->q, descend);
return p->size;
}
```