# 2023q1 Homework1 (lab0)
contributed by < `qiaoyi213` >
:::danger
注意項目符號的使用,標題用 ==`# `== 開頭,分項就該以 ==`## `== 開頭,並該維護階層關係。
登記在 https://hackmd.io/@sysprog/linux2023-homework1 的網址,要用「固定網址」(參見 用固定網址發布筆記),也就是如 https://hackmd.io/@itsme/XXXX 的形式,設定公開發表。
:notes: jserv
:::
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
$ lscpu
Architecture: aarch64
CPU op-mode(s): 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: 0x00
Model name: -
Model: 0
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 1
Stepping: 0x0
BogoMIPS: 48.00
```
[作業敘述與要求](https://hackmd.io/@sysprog/linux2023-lab0)
:::danger
不用抄題目,善用超連結。
:notes: jserv
:::
:::spoiler 進度表
- [x] `q_new`
- [x] `q_free`
- [x] `q_insert_head`
- [x] `q_insert_tail`
- [x] `q_remove_head`
- [x] `q_remove_tail`
- [x] `q_size`
- [x] `q_delete_mid`
- [x] `q_delete_dup`
- [x] `q_swap`
- [x] `q_reverse`
- [x] `q_reverseK`
- [x] `q_sort`
- [x] `q_descend`
- [x] `q_merge`
:::
## 閱讀 [你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list)
### indirect pointer
一開始並不懂 indirect pointer 的用意,看了許久還是不理解為什麼這麼做。
:::danger
看不懂就該列出教材哪裡沒寫好。不要說「大概懂」,無法應用和指出如何改進就是不懂。
:notes: jserv
:::
參考 [Mes 的筆記](
https://hackmd.io/@Mes/mes_note/https%3A%2F%2Fhackmd.io%2F%40Mes%2FThe_mind_behind_Linux) 後就大概懂 indirect pointer 的技巧,但在實作上可能還需要熟悉。
## 常用結構與巨集
寫到一半發現真的對我自己寫的東西很不確定,所以回來重新審視 `queue.h` 以及 `list.h`。
:::warning
幻滅是成長的開始。
:notes: jserv
:::
### list_head
```c
struct list_head {
struct list_head *prev;
struct list_head *next;
};
```
### element_t
```c
typedef struct {
char *value;
struct list_head list;
} element_t;
```
## 開發過程
### Null pointer deference
:::warning
在執行下列命令時
```shell
$cppcheck queue.c
```
多次出現 `Null pointer deference` 的錯誤,目前還不知道怎麼解決。
例如:
>queue.c:71:22: error: Null pointer dereference: (struct element_t*)0 [nullPointer]
element_t *tmp = list_first_entry(head, element_t, list);
:::
## 實作 queue.[ch]
### `q_new`
參考 `list.h` 中的 `INIT_LIST_HEAD` 函式,此函式是將 `linked list` 中的 `prev` 指標以及 `next` 指標指向自己,形成一個環形的 `linked list`。
```c
static inline void INIT_LIST_HEAD(struct list_head *head)
{
head->next = head;
head->prev = head;
}
```
```c
struct list_head *q_new()
{
struct list_head *li = malloc(sizeof(struct list_head));
if(li)
INIT_LIST_HEAD(li)
return li;
}
```
### `q_free`
從頭開始依序迭代所有的節點,移除該節點後就釋放該節點記憶體。
利用 `list.h` 中的巨集 `list_for_each_entry_safe` 實作 `q_free`。
:::danger
注意[資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary)
:notes: jserv
:::
:::success
已將「遍歷」修正為「迭代」
:::
```c
void q_free(struct list_head *l)
{
element_t *entry, *safe;
list_for_each_entry_safe(entry, safe, l, list)
q_release_element(entry);
free(l);
}
```
### `q_insert_head`
使用 `list_add` 巨集新增節點
```c
bool q_insert_head(struct list_head *head, char *s)
{
if(!head)
return false;
element_t *new = malloc(sizeof(element_t));
if(!new)
return false;
new->value = strdup(s);
if(!new->value) {
free(new);
return false;
}
list_add(&new->list, head);
return true;
}
```
### `q_insert_tail`
與 `q_insert_head` 實作方法相似,只是將 `list_add()` 更換為 `list_add_tail()`
```c
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *new = malloc(sizeof(element_t));
if (!new)
return false;
new->value = strdup(s);
if (!new->value) {
free(new);
return false;
}
list_add_tail(&new->list, head);
return true;
}
```
### `q_size`
遍歷所有節點並且記錄 `linked list` 的大小。
```c
int q_size(struct list_head *head)
{
if(!head)
return 0;
int size = 0;
struct list_head *node;
list_for_each(node, head)
size++;
return size;
}
```
### `q_remove_head`
:::warning
目前測試結果仍有錯誤:Probably not constant time or wrong implementation
:::
```c
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if(!head || list_empty(head))
return NULL;
element_t *tmp = list_entry(head->next, element_t, list);
if(sp){
strncpy(sp, tmp->value, bufsize-1);
sp[bufsize-1] = '\0';
}
list_del(&tmp->list);
return tmp;
}
```
### `q_remove_tail`
```c
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if(!head || list_empty(head))
return NULL;
element_t *tmp = list_entry(head->prev, element_t, list);
if(sp){
strncpy(sp, tmp->value, bufsize-1);
sp[bufsize-1] = '\0';
}
list_del(&tmp->list);
return tmp;
}
```
### `q_delete_mid`
使用 `slow` 指標以及 `fast` 指標
每次移動 `slow` 指標一次、`fast` 指標兩次,即可找到中間的位置。
```c
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;
slow = head->next;
fast = head->next;
while (fast != head && fast->next != head) {
slow = slow->next;
fast = fast->next->next;
}
list_del(slow);
element_t *e = list_entry(fast, element_t, list);
list_del(fast);
free(e->value);
free(e);
return true;
}
```
### `q_delete_dup`
```c
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if(!head)
return false;
if(list_empty(head) || list_is_singular(head))
return true;
bool is_dup = false;
element_t *node, *safe;
list_for_each_entry_safe(node, safe, head, list){
if(&safe->list != head && strcmp(node->value, safe->value) == false){
list_del(&node->list);
q_release_element(node);
is_dup = true;
} else if ( is_dup ){
list_del(&node->list);
q_release_element(node);
is_dup = false;
}
}
return true;
}
```
### `q_reverse`
```c
void q_reverse(struct list_head *head)
{
if(!head || list_empty(head))
return;
struct list_head *node, *safe;
list_for_each_safe(node, safe, head)
list_move(node,head);
return;
}
```
### `q_reverseK`
```c
void q_reverseK(struct list_head *head, int k)
{
// https://leetcode.com/problems/reverse-nodes-in-k-group/
if(!head || list_empty(head) || k < 2)
return;
int list_length = q_size(head);
for(struct list_head *now = head->next;now != head && now->next != head;now = now->next) {
struct list_head **curr = &now->next, *tmp = now->prev;
for(int i=1;i<k;i++)
if(list_length >= k)
list_move(*curr, tmp);
}
}
```
### `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;
for(struct list_head *curr = head->next; curr != head && curr->next != head; curr = curr->next)
list_move(curr, curr->next);
}
```
### `merge_two_list`
參考 [你所不知道的 C 語言: linked list 和非連續記憶體](https://hackmd.io/@sysprog/c-linked-list#%E6%A1%88%E4%BE%8B%E6%8E%A2%E8%A8%8E-LeetCode-21-Merge-Two-Sorted-Lists) 後重新實作一遍。
將兩個已排序好的 `linked list` 合併並排序。
:::warning
疑惑:在原本的範例中是使用 `uintptr_t`,我不清楚為什麼這裡是使用 `u_int64_t` 才對。
:::
```c
struct list_head *merge_two_list(struct list_head *l1, struct list_head *l2)
{
struct list_head *head, **ptr, **node;
head = NULL;
ptr = &head;
for(node = NULL; l1 && l2; *node=(*node)->next){
if(strcmp(list_entry(l1, element_t, list)->value,
list_entry(l2, element_t, list)->value) < 0)
node = &l1;
else
node = &l2;
*ptr = *node;
ptr = &(*ptr)->next;
}
*ptr = (struct list_head *) ((u_int64_t) l1 | (u_int64_t) l2);
return head;
}
```
### `merge_sort`
實作 merge sort,並且使用與 `q_delete_mid` 相同的方法找位於 `queue` 中間的節點以分治。
```c
static inline struct list_head merge_sort(struct list_head *head)
{
if(!head || !head->next)
return head;
struct list_head *fast, *slow;
fast = head->next;
slow = head->next;
while (slow != fast && slow->next != fast) {
slow = slow->next;
fast = fast->next->next;
}
struct list_head *left, *right;
right = slow->next;
slow->next = NULL;
left = merge_sort(head);
right = merge_sort(right);
return merge_list(left, right);
}
```
### `q_sort`
首先要先將 `linked list` 斷開連接變成 `singular linked list` 之後進行 merge sort 之後再重新連接。
```c
void q_sort(struct list_head *head)
{
if (!head || list_empty(head))
return;
head->prev->next = NULL;
head->next = mergesort(head->next);
struct list_head *current = head, *next = head->next;
while (next) {
next->prev = current;
current = next;
next = next->next;
}
current->next = head;
head->prev = current;
}
```
## `q_merge`
```c
int q_merge(struct list_head *head)
{
// https://leetcode.com/problems/merge-k-sorted-lists/
if (!head || list_empty(head))
return 0;
if (list_is_singular(head))
return q_size(list_first_entry(head, queue_contex_t, chain)->q);
int queue_size = 0;
queue_contex_t *first, *second;
first = list_first_entry(head, queue_contex_t, chain),
second = list_entry(first->chain.next, queue_contex_t, chain);
while (second->q) {
queue_size = q_size(merge_two_list(first->q, second->q));
second->q = NULL;
list_move_tail(&second->chain, head);
second = list_entry(first->chain.next, queue_contex_t, chain);
}
return queue_size;
}
```
### `q_descend`
```c
int q_descend(struct list_head *head)
{
// https://leetcode.com/problems/remove-nodes-from-linked-list/
element_t *entry;
list_for_each_entry (entry, head, list) {
struct list_head *prev = entry->list.prev, *safe = prev->prev;
for (; prev != head; prev = safe, safe = safe->prev) {
element_t *prev_entry = list_entry(prev, element_t, list);
if (strcmp(prev_entry->value, entry->value) >= 0)
break;
list_del(prev);
q_release_element(prev_entry);
}
}
return 0;
}
```
:::info
`make test` 分數:71/100
:::