---
# System prepended metadata

title: 2025q1 Homework1 (lab0)

---

# 2025q1 Homework1 (lab0)
contributed by < Cactex >
    
{%hackmd NrmQUGbRQWemgwPfhzXj6g %}
    
# 開發環境

 ```
 $ hostnamectl
Operating System: Ubuntu 24.04.1 LTS              
Kernel: Linux 6.11.0-19-generic
              
 $ gcc --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

$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):                   8
On-line CPU(s) list:      0-7
Vendor ID:                GenuineIntel
Model name:               Intel(R) Core(TM) i5-10200H CPU @ 2.40GHz
CPU family:               6
Model:                    165
Thread(s) per core:       2
Core(s) per socket:       4
Socket(s):                1
Stepping:                 2
CPU(s) scaling MHz:       93%
CPU max MHz:              4100.0000
CPU min MHz:              800.0000
BogoMIPS:                 4800.00   
L1d:                      128 KiB (4 instances)
L1i:                      128 KiB (4 instances)
L2:                       1 MiB (4 instances)
L3:                       8 MiB (1 instance)             
NUMA node(s):             1
NUMA node0 CPU(s):        0-7
```
# 實作指定的佇列操作
 
 在實作 ```queue.c``` 的函式之前，應閱讀 ```queue.h```中對應之函式說明。
 
 並且在執行 `git commit` 前，使用 clang-format 工具
 ```shell
 $ clang-format -i *.[ch] 
 ``` 
 來調整程式碼以符合作業要求的風格
 
 ## q_new
 > commit [0e4d305
](https://github.com/Cactex/lab0-c/commit/0e4d305c199a7140d3afdef48aa6425bfcea9bee)

建立空佇列並且將 `next` 和 `prev` 都指向自己
```c
struct list_head *q_new()
{
    struct list_head *head = malloc(sizeof(struct list_head));
    if (!head)
        return NULL;
    INIT_LIST_HEAD(head);
    return head;
}
```

 ## q_free
 > commit [e37136f
](https://github.com/Cactex/lab0-c/commit/e37136fd93b69ab0fcb3b5c158979b2d9935296c)

釋放佇列`element_t` 使用的所有記憶體空間，若傳入值 `head` 是 `NULL`則不需要做任何操作

```c
void q_free(struct list_head *head)
{
    if (!head)
        return;
    if (list_empty(head)) {
        free(head);
        return;
    }
    struct list_head *node, *safe;
    list_for_each_safe (node, safe, head) {
        element_t *target_ele = list_entry(node, element_t, list);
        q_release_element(target_ele);
    }
    free(head);
}
```

 ## q_insert_head
 > commit [0e4d305
](https://github.com/Cactex/lab0-c/commit/0e4d305c199a7140d3afdef48aa6425bfcea9bee)

建立新的佇列元素並且將其插入到 `head` 和 `head->next` 中間
需要注意的是如果 `head` 為 `NULL` 或呼叫 `malloc()` 分配記憶體失敗則須回傳 `false`

```c
bool q_insert_head(struct list_head *head, char *s)
{
    if (!head)
        return false;
    element_t *new_element = malloc(sizeof(element_t));
    if (!new_element)
        return false;
    new_element->value = malloc(strlen(s) + 1);
    if (!new_element->value) {
        q_release_element(new_element);
        return false;
    }
    strlcpy(new_element->value, s, strlen(s) + 1);
    list_add(&new_element->list, head);
    return true;
}
```
 
  ## q_insert_tail
 > commit [0e4d305
](https://github.com/Cactex/lab0-c/commit/0e4d305c199a7140d3afdef48aa6425bfcea9bee)

基本上和 `q_insert_head` 一樣，只是將元素加到佇列尾端

```c
bool q_insert_tail(struct list_head *head, char *s)
{
    if (!head)
        return false;
    element_t *new_element = malloc(sizeof(element_t));
    if (!new_element)
        return false;
    new_element->value = malloc(strlen(s) + 1);
    if (!new_element->value) {
        q_release_element(new_element);
        return false;
    }
    strlcpy(new_element->value, s, strlen(s) + 1);
    list_add_tail(&new_element->list, head);
    return true;
}
```

  ## q_remove_head
 > commit [0e4d305
](https://github.com/Cactex/lab0-c/commit/0e4d305c199a7140d3afdef48aa6425bfcea9bee)

"移出"佇列中第一個元素，並且回傳該地址
並將元素的值複製到 `sp` ，大小由 `bufsize` 決定，須注意 `bufsize` 中包含 `\0`


```c
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
    if (!head || list_empty(head))
        return NULL;
    struct list_head *target_node = head->next;
    element_t *target_ele = list_entry(target_node, element_t, list);
    strlcpy(sp, target_ele->value, bufsize);
    list_del(target_node);
    return target_ele;
}
```

  ## q_remove_tail
 > commit [0e4d305
](https://github.com/Cactex/lab0-c/commit/0e4d305c199a7140d3afdef48aa6425bfcea9bee)

"移出"佇列中最後一個元素，並且回傳該地址
並將元素的值複製到 `sp` ，大小由 `bufsize` 決定，須注意 `bufsize` 中包含 `\0`


```c
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
    if (!head || list_empty(head))
        return NULL;
    struct list_head *target_node = head->prev;
    element_t *target_ele = list_entry(target_node, element_t, list);
    strlcpy(sp, target_ele->value, bufsize);
    list_del(target_node);
    return target_ele;
}
```

  ## q_delete_mid
 > commit [1691d92
](https://github.com/Cactex/lab0-c/commit/1691d925d8ea9ab32170fe98bf364caa2a33e610)

若 `head` 為 `NULL` 或 佇列為空則回傳 `false`
實做方法為兩個節點，一個向前 traverse，另外一個向後，直到兩個節點相遇

**討論**
另一種作法為兩節點同方向 traverse ，其中一個節點速度為另一個兩倍，直到節點相遇

```c
bool q_delete_mid(struct list_head *head)
{
    if (!head || list_empty(head))
        return false;
    struct list_head *nprev = head->prev;
    struct list_head *nnext = head->next;
    while ((nprev != nnext) && (nnext->next != nprev)) {
        nprev = nprev->prev;
        nnext = nnext->next;
    }
    list_del(nprev);
    q_release_element(list_entry(nprev, element_t, list));
    return true;
}
```

 ## q_delete_dup
 > commit [9ae6e83
](https://github.com/Cactex/lab0-c/commit/9ae6e83a58e5669e90faf7eb2b6e8dce2be1018a)

前提預設為 sorted list 

```c
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 *node, *safe;
    bool isdup = 0;
    list_for_each_safe (node, safe, head) {
        element_t *curr = list_entry(node, element_t, list);
        element_t *next = list_entry(safe, element_t, list);
        if(strcmp(curr->value, next->value) == 0){
            list_del(&curr->list);
            q_release_element(curr);
            isdup = true;
            continue;
        }
        if(isdup){
            list_del(&tmp->list);
            q_release_element(curr);
            isdup = false;
        }
    }
    return true;
}
```

實做以上程式碼遇到 `0xdeadbeef`
使用Valgrind工具發現問題在 `strcmp` ，以上程式碼會比較現在節點和下個節點的value，導致訪問到 `head` 節點的 value
```shell
 $ valgrind -q --leak-check=full ./qtest
...
cmd> ih 4
l = [4 3 2 2]
cmd> dedup
==104311== Invalid read of size 1
==104311==    at 0x4850367: strcmp (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==104311==    by 0x110141: q_delete_dup (queue.c:142)
==104311==    by 0x10C3B6: do_dedup (qtest.c:467)
==104311==    by 0x10E76D: interpret_cmda (console.c:181)
==104311==    by 0x10ED32: interpret_cmd (console.c:201)
==104311==    by 0x10F81C: run_console (console.c:659)
==104311==    by 0x10DB5C: main (qtest.c:1444)
==104311==  Address 0xdeadbeef is not stack'd, malloc'd or (recently) free'd
==104311== 
Segmentation fault occurred.  You dereferenced a NULL or invalid pointer==104311== 2 bytes in 1 blocks are still reachable in loss record 1 of 52
 ``` 
 
 補上就沒問題了
 ```diff
+if(safe != head){
            element_t *next = list_entry(safe, element_t, list);
            if(strcmp(curr->value, next->value) == 0){
                list_del(&curr->list);
                q_release_element(curr);
                isdup = true;
                continue;
            }
+        }
 }
```

  ## q_swap
 > commit [b29d3c2
](https://github.com/Cactex/lab0-c/commit/b29d3c26bdfee783714c774b522ee84f3c38a680)

目標是將佇列中每對相鄰的節點交換，可以想像成把第一個節點放到第二個節點後面

**討論**
另一種作法為兩節點同方向 traverse ，其中一個節點速度為另一個兩倍，直到節點相遇

```c
bool q_delete_mid(struct list_head *head)
{
    if (!head || list_empty(head))
        return false;
    struct list_head *nprev = head->prev;
    struct list_head *nnext = head->next;
    while ((nprev != nnext) && (nnext->next != nprev)) {
        nprev = nprev->prev;
        nnext = nnext->next;
    }
    list_del(nprev);
    q_release_element(list_entry(nprev, element_t, list));
    return true;
}
```

## q_reverse
 > commit [1683868
](https://github.com/Cactex/lab0-c/commit/168386826d282417bf7bdc24ce87f627bd7ff913)

目標是將佇列每個元素的順序翻轉，如果佇列為空或 `NULL` 不需要調整。
並且不能 allocate 或 free 任何佇列元素，僅調整原有元素之順序。

想法為將最後面的元素一直向前放

```c
void q_reverse(struct list_head *head)
{
    for (struct list_head *last = head->prev, *front = head->next,
                          *current = head;
         last != front; last = head->prev, current = current->next) {
        head->prev = last->prev;
        last->prev->next = head;

        current->next->prev = last;
        last->next = current->next;
        last->prev = current;
        current->next = last;
    }
}
```

## q_reverseK
 > commit [1683868
](https://github.com/Cactex/lab0-c/commit/168386826d282417bf7bdc24ce87f627bd7ff913)

和q_reverse接近，但翻轉的大小為 k ，若佇列大小不足 k 則不須翻轉。
佇列為空或長度為1或 k 的大小為則不需要作任何動作。

若佇列長度和 k 大小一致，則和 `q_reverse` 相同。
一般 case 的實做則可以透過 `list_cut_position` 去切分佇列，將其翻轉後再接回原本佇列。

```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 == 1 || list_is_singular(head))
        return;
    int length = q_size(head);
    if (length == k)
        q_reverse(head);
    int counter = 0;
    LIST_HEAD(tmp);
    LIST_HEAD(reverse);
    struct list_head *node, *safe;
    list_for_each_safe (node, safe, head) {
        if (++counter == k) {
            list_cut_position(&tmp, head, node);
            q_reverse(&tmp);
            list_splice_tail_init(&tmp, &reverse);
            counter = 0;
        }
    }
    list_splice_init(&reverse, head);
}
```

## q_sort
> commit [14be610
](https://github.com/Cactex/lab0-c/commit/14be610687ae1b6e50847ae583255bb1745b7a94)

用 top down 的方法去實做 q_sort 
```c
struct list_head *merge_sort(struct list_head *head)
{
    if (!head || !head->next)
        return head;
    struct list_head *slow = head;
    for (const struct list_head *fast = head->next; fast && fast->next;
         fast = fast->next->next)
        slow = slow->next;
    struct list_head *mid = slow->next;
    // remove mid and its prev link
    slow->next = NULL;
    struct list_head *left = merge_sort(head), *right = merge_sort(mid);
    return mergeTwoLists(left, right);
}

/* Sort elements of queue in ascending/descending order */
void q_sort(struct list_head *head, bool descend)
{
    if (!head || list_empty(head))
        return;

    if (list_is_singular(head))
        return;

    head->prev->next = NULL;
    head->next = merge_sort(head->next);

    // reconnect list
    struct list_head *c = head, *n = head->next;
    while (n) {
        n->prev = c;
        c = n;
        n = n->next;
    }
    c->next = head;
    head->prev = c;


    if (descend)
        q_reverse(head);
    return;
}
```

## q_ascend
 > commit [1f5fb10
](https://github.com/Cactex/lab0-c/commit/1f5fb100afd20766ee9c7c03a8a0c76cea8cbac7)

透過移除佇列元素讓佇列達到升冪排列(可以有相同值)，空佇列和佇列長度為1不需要更改

將 `list.h` 中的 `list_for_each_entry_safe` 改寫，以達到逆向走訪的效果。

```c
int q_ascend(struct list_head *head)
{
    if (!head || list_empty(head))
        return 0;
    if (list_is_singular(head))
        return 1;
    element_t *safe, *node;
    char const *tmpch = (list_entry(head->prev, element_t, list))->value;
    for (node = list_entry((head)->prev, typeof(*node), list),
        safe = list_entry(node->list.prev, typeof(*node), list);
         &node->list != (head);
         node = safe, safe = list_entry(safe->list.prev, typeof(*node), list)) {
        if (strcmp(node->value, tmpch) > 0) {
            list_del(&node->list);
            continue;
        }
        tmpch = node->value;
    }
    return q_size(head);
}
```

## q_descend
 > commit [1f5fb10
](https://github.com/Cactex/lab0-c/commit/1f5fb100afd20766ee9c7c03a8a0c76cea8cbac7)

透過移除佇列元素讓佇列達到降冪排列(可以有相同值)，空佇列和佇列長度為1不需要更改

內容和q_ascend差不多，只是變成降冪排列

```c
int q_descend(struct list_head *head)
{
    if (!head || list_empty(head))
        return 0;
    if (list_is_singular(head))
        return 1;
    element_t *safe, *node;
    char const *tmpch = (list_entry(head->prev, element_t, list))->value;
    for (node = list_entry((head)->prev, typeof(*node), list),
        safe = list_entry(node->list.prev, typeof(*node), list);
         &node->list != (head);
         node = safe, safe = list_entry(safe->list.prev, typeof(*node), list)) {
        if (strcmp(node->value, tmpch) < 0) {
            list_del(&node->list);
            continue;
        }
        tmpch = node->value;
    }
    return q_size(head);
}
```

## q_merge
 > commit [b2aabc4](https://github.com/Cactex/lab0-c/commit/b2aabc4e19c23602158245d9a6c627e117aa678f)

 > commit [d76a488](https://github.com/Cactex/lab0-c/commit/d76a488c70bbfa64402ae80cfab83f23af401072)

將 queue 中多個 sorted lists 合併

需要注意的是一開始給的佇列中每個元素都包含一個 queue ，需要用 `list_entry` 以找到整個結構體中的 sorted list。

另外有寫 `merge_2_queue` 去完成  

```c
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 *list1 = list_entry(head->next, queue_contex_t, chain);
    struct list_head *chain_queue = head->next->next;
    if (chain_queue == head)
        return list1->size;
    int count = list1->size;
    for (; chain_queue != head; chain_queue = chain_queue->next) {
        queue_contex_t *merge_list =
            list_entry(chain_queue, queue_contex_t, chain);
        if (!merge_list->q || list_empty(merge_list->q)) {
            continue;
        }
        count = count + merge_list->size;
        merge_2_queue(list1->q, merge_list->q, descend);
    }
    return count;
}
```

# git rebase
```shell
$ git rebase upstream/master
自動合併 queue.c11)
衝突（內容）：合併衝突於 queue.c
error: 不能套用 b2aabc4... Implement q_merge and fixed bugs
提示：Resolve all conflicts manually, mark them as resolved with
提示："git add/rm <conflicted_files>", then run "git rebase --continue".
提示：You can instead skip this commit: run "git rebase --skip".
提示：To abort and get back to the state before "git rebase", run "git rebase --abort".
不能套用 b2aabc4... Implement q_merge and fixed bugs

```
在手動解決衝突後
```shell
$ git add queue.c
$ 

```