Try   HackMD

2025q1 Homework1 (lab0)

contributed by < timsong1 >

作業書寫規範:

  • 無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利)
  • 文字訊息 (尤其是程式執行結果) 請避免用圖片來表示,否則不好搜尋和分類
  • 共筆書寫請考慮到日後協作,避免過多的個人色彩,用詞儘量中性
  • 不要在筆記內加入 [TOC] : 筆記左上方已有 Table of Contents (TOC) 功能,不需要畫蛇添足
  • 不要變更預設的 CSS 也不要加入任何佈景主題: 這是「開發紀錄」,用於評分和接受同儕的檢閱
  • 在筆記中貼入程式碼時,避免非必要的行號,也就是該手動將 c=cpp= 變更為 ccpp。行號只在後續討論明確需要行號時,才要出現,否則維持精簡的展現。可留意「你所不知道的 C 語言: linked list 和非連續記憶體」裡頭程式碼展現的方式
  • HackMD 不是讓你張貼完整程式碼的地方,GitHub 才是!因此你在開發紀錄只該列出關鍵程式碼 (善用 diff 標示),可附上對應 GitHub commit 的超連結,列出程式碼是為了「檢討」和「便於他人參與討論」
  • 留意科技詞彙的使用,請參見「資訊科技詞彙翻譯」及「詞彙對照表
  • 不要濫用 :::info, :::success, :::warning 等標示,儘量用清晰的文字書寫。:::danger 則僅限授課教師作為批注使用
  • 避免過多的中英文混用,已有明確翻譯詞彙者,例如「鏈結串列」(linked list) 和「佇列」(queue),就使用該中文詞彙,英文則留給變數名稱、人名,或者缺乏通用翻譯詞彙的場景
  • 在中文敘述中,使用全形標點符號,例如該用「,」,而非 ","。注意書名號的使用,即 ,非「小於」和「大於」符號
  • 避免使用不必要的 emoji 字元

開發環境

展開
$ gcc --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
Copyright (C) 2023 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):                   8
  On-line CPU(s) list:    0-7
Vendor ID:                GenuineIntel
  Model name:             Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
    CPU family:           6
    Model:                126
    Thread(s) per core:   2
    Core(s) per socket:   4
    Socket(s):            1
    Stepping:             5
    CPU(s) scaling MHz:   33%
    CPU max MHz:          3600.0000
    CPU min MHz:          400.0000
    BogoMIPS:             2380.80
    Flags:                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge m
                          ca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 s
                          s ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc 
                          art arch_perfmon pebs bts rep_good nopl xtopology nons
                          top_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq 
                          dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 
                          xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_d
                          eadline_timer aes xsave avx f16c rdrand lahf_lm abm 3d
                          nowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_
                          enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsb
                          ase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid avx512
                          f avx512dq rdseed adx smap avx512ifma clflushopt intel
                          _pt avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec 
                          xgetbv1 xsaves split_lock_detect dtherm ida arat pln p
                          ts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req v
                          nmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes v
                          pclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq r
                          dpid fsrm md_clear flush_l1d arch_capabilities
Virtualization features:  
  Virtualization:         VT-x
Caches (sum of all):      
  L1d:                    192 KiB (4 instances)
  L1i:                    128 KiB (4 instances)
  L2:                     2 MiB (4 instances)
  L3:                     6 MiB (1 instance)
NUMA:                     
  NUMA node(s):           1
  NUMA node0 CPU(s):      0-7
Vulnerabilities:          
  Gather data sampling:   Mitigation; Microcode
  Itlb multihit:          KVM: Mitigation: VMX disabled
  L1tf:                   Not affected
  Mds:                    Not affected
  Meltdown:               Not affected
  Mmio stale data:        Mitigation; Clear CPU buffers; SMT vulnerable
  Reg file data sampling: Not affected
  Retbleed:               Mitigation; Enhanced IBRS
  Spec rstack overflow:   Not affected
  Spec store bypass:      Mitigation; Speculative Store Bypass disabled via prct
                          l
  Spectre v1:             Mitigation; usercopy/swapgs barriers and __user pointe
                          r sanitization
  Spectre v2:             Mitigation; Enhanced / Automatic IBRS; IBPB conditiona
                          l; RSB filling; PBRSB-eIBRS SW sequence; BHI SW loop, 
                          KVM SW loop
  Srbds:                  Mitigation; Microcode
  Tsx async abort:        Not affected

修改 queue.c

q_new

Create an empty queue

struct list_head *q_new()
{
    struct list_head *head =
        (struct list_head *) malloc(sizeof(struct list_head));
    if (head) {
        head->prev = head;
        head->next = head;
    }
    return head;
}

q_free

Free all storage used by queue

void q_free(struct list_head *head)
{
    if (!head)
        return;
    if (list_empty(head)) {
        free(head);
        return;
    }
    while (!list_empty(head)) {
        struct list_head *curr = head->next;
        list_del(curr);
        element_t *element = container_of(curr, element_t, list);
        q_release_element(element);
    }
    free(head);
}

q_insert_head

Insert an element at head of queue

bool q_insert_head(struct list_head *head, char *s)
{
    if (!head || !s)
        return false;
    const char *cs = s;
    element_t *element = (element_t *) malloc(sizeof(element_t));
    if (!element)
        return false;
    element->value = strdup(cs);
    if (!element->value) {
        free(element);
        return false;
    }
    list_add(&element->list, head);
    return true;
}

q_insert_tail

Insert an element at tail of queue

bool q_insert_head(struct list_head *head, char *s)
{
    if (!head || !s)
        return false;
    const char *cs = s;
    element_t *element = (element_t *) malloc(sizeof(element_t));
    if (!element)
        return false;
    element->value = strdup(cs);
    if (!element->value) {
        free(element);
        return false;
    }
    list_add_tail(&element->list, head);
    return true;
}

q_remove_head

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))
        return NULL;
    element_t *curr = container_of(head->next, element_t, list);
    curr->list.next->prev = head;
    head->next = curr->list.next;
    curr->list.next = curr->list.prev = NULL;
    if (sp && bufsize > 0) {
        strncpy(sp, curr->value, bufsize - 1);
        sp[bufsize - 1] = '\0';
    }
    return curr;
}

q_remove_tail

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))
        return NULL;
    element_t *curr = container_of(head->prev, element_t, list);
    curr->list.prev->next = head;
    head->prev = curr->list.prev;
    curr->list.next = curr->list.prev = NULL;
    if (sp && bufsize > 0) {
        strncpy(sp, curr->value, bufsize - 1);
        sp[bufsize - 1] = '\0';
    }
    return curr;
}

q_size

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

Delete the middle node in queue

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

q_delete_dup

Delete all nodes that have duplicate string

寫了一個 helper fuction : delete_linked_list() 用來刪除佇列中特定範圍的節點

void delete_linked_list(struct list_head *head,
                        struct list_head *from,
                        struct list_head *to)
{
    struct list_head *node, *safe;
    bool flag = false;
    list_for_each_safe (node, safe, head) {
        if (node == to)
            return;
        if (node == from)
            flag = true;
        if (flag) {
            list_del(node);
            q_release_element(list_entry(node, element_t, list));
        }
    }
}
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;
    if (head->next == head->prev)
        return true;
    struct list_head *curr = head->next, *next = curr->next;
    while (curr != head && next != head) {
        const element_t *element = list_entry(curr, element_t, list);
        if (strcmp(element->value, list_entry(next, element_t, list)->value) ==
            0) {
            while (next != head) {
                if (strcmp(list_entry(next, element_t, list)->value,
                           element->value) != 0)
                    break;
                next = next->next;
            }
            delete_linked_list(head, curr, next);
            curr = next;
            next = next->next;
            continue;
        }
        curr = curr->next;
        next = curr->next;
    }
    return true;
}

q_swap

Swap every two adjacent nodes

void q_swap(struct list_head *head)
{
    // https://leetcode.com/problems/swap-nodes-in-pairs/
    if (!head || head->next == head->prev) {
        return;
    }
    struct list_head *curr, *next;
    curr = head->next;
    next = curr->next;
    while (curr != head && next != head) {
        curr->prev->next = next;  // change the curr and next node 
        next->next->prev = curr;
        curr->next = next->next;
        next->prev = curr->prev;
        curr->prev = next;
        next->next = curr;
        curr = curr->next;    // move the curr and next pointer forward
        next = curr->next;
    }
}

q_reverse

Reverse elements in queue

void q_reverse(struct list_head *head)
{
    if (!head || head->next == head->prev)
        return;
    struct list_head *node = NULL, *safe;
    list_for_each_safe (node, safe, head) {
        list_move(node, head);
    }
}

q_reverseK

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/
    // Assume k is a positive integer and is less than or equal to the length of
    // the linked list.
    if (!head || head->prev == head->next)
        return;
    int len = 0;
    struct list_head *li = head->next, *start = head, *stop;
    while (li != head) {
        len++;
        if (len == k) {
            stop = li->next;
            struct list_head *curr = start->next, *next = curr->next,
                             *next_start = stop;
            while (curr != stop) {
                while (next != stop) {
                    curr->prev->next = next;
                    next->next->prev = curr;
                    curr->next = next->next;
                    next->prev = curr->prev;
                    curr->prev = next;
                    next->next = curr;
                    next = curr->next;
                }
                curr = start->next;
                next = curr->next;
                stop = stop->prev;
            }
            len = 0;
            li = start = next_start->prev;
        }
        li = li->next;
    }
}

想試著修改 reverseK(),在函式裡面可以呼叫 reverse()
程式(以下)還有問題,待編輯

void q_reverseK(struct list_head *head, int k)
{
    // https://leetcode.com/problems/reverse-nodes-in-k-group/
    // Assume k is a positive integer and is less than or equal to the length of
    // the linked list.
    if (!head || head->prev == head->next)
        return;
    int len = 0;
    struct list_head *li = head->next, *start = head,
                     *new_head = head, *new_start;
    while (li != head) {
        len++;
        if (len == k) {
            new_start = li->next;
            start->next->prev = new_head;
            new_start->prev->next = new_head;
            new_head->next = start->next;
            new_head->prev = new_start->prev;
            q_reverse(new_head);
            new_head->next->prev = start;
            new_head->prev->next = new_start;
            len = 0;
            start = new_start;
            li = new_start;
            continue;
        }
        li = li->next;
    }
}


q_sort

Sort elements of queue in ascending/descending order

目前此方法是採遞迴 top-down merge sort 來排序
正在嘗試引用 lib/list_sort.c

struct list_head *merge_sort(struct list_head *head, bool descend)
{
    if (!head || !head->next)
        return head;
    struct list_head *slow, *fast, *left, *right;
    slow = fast = left = head;
    // left = head->next;
    while (fast && fast->next && fast->next->next) {
        slow = slow->next;
        fast = fast->next->next;
    }
    right = slow->next;
    slow->next = NULL;
    right->prev = NULL;
    left = merge_sort(left, descend);
    right = merge_sort(right, descend);
    struct list_head **ptr = &head;
    for (;;) {
        if (!left) {
            *ptr = right;
            break;
        }
        if (!right) {
            *ptr = left;
            break;
        }
        if (strcmp(list_entry(left, element_t, list)->value,
                   list_entry(right, element_t, list)->value) > 0) {
            if (descend) {
                *ptr = left;
                ptr = &left->next;
                left = left->next;
            } else {
                *ptr = right;
                ptr = &right->next;
                right = right->next;
            }
        } else {
            if (descend) {
                *ptr = right;
                ptr = &right->next;
                right = right->next;
            } else {
                *ptr = left;
                ptr = &left->next;
                left = left->next;
            }
        }
    }
    return head;
}
/* Sort elements of queue in ascending/descending order */
void q_sort(struct list_head *head, bool descend)
{
    if (!head || head->next == head->prev)
        return;
    head->prev->next = NULL;
    head->next = merge_sort(head->next, descend);
    // re-link prev pointer
    struct list_head *li = head->next, *prev = head;
    while (li) {
        li->prev = prev;
        li = li->next;
        prev = prev->next;
    }
    prev->next = head;
    head->prev = prev;
}


q_ascend

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/
    int count = 1;
    if (!head || head->prev == head->next)
        return 0;
    struct list_head *curr = head->prev, *min = head->prev, *prev = curr->prev;
    while (curr->prev != head) {
        element_t *previous;
        const element_t *min_element;
        previous = list_entry(prev, element_t, list);
        min_element = list_entry(min, element_t, list);
        if (strcmp(previous->value, min_element->value) > 0) {
            list_del(prev);
            q_release_element(previous);
            prev = curr->prev;
        } else {
            min = curr = prev;
            count++;
            prev = curr->prev;
        }
    }
    return count;
}

q_descend

Remove every node which has a node with a strictly greater value anywhere to the right side of it

int q_descend(struct list_head *head)
{
    // https://leetcode.com/problems/remove-nodes-from-linked-list/
    int count = 1;
    if (!head || head->prev == head->next)
        return 0;
    struct list_head *curr = head->prev, *max = head->prev, *prev = curr->prev;
    while (curr->prev != head) {
        element_t *previous;
        const element_t *max_element;
        previous = list_entry(prev, element_t, list);
        max_element = list_entry(max, element_t, list);
        if (strcmp(previous->value, max_element->value) < 0) {
            list_del(prev);
            q_release_element(previous);
            prev = curr->prev;
        } else {
            max = curr = prev;
            count++;
            prev = curr->prev;
        }
    }
    return count;
}

q_merge

Merge all the queues into one sorted queue, which is in ascending/descending order

queue_contex_t 資料結構中用來串接每個 queue 的 chain 一樣也是會有一個 head 指標當作 dummy node (或叫 sentinel node)

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 *context = list_entry(head->next, queue_contex_t, chain);
    struct list_head *first_queue;
    context->q->prev->next = NULL;
    struct list_head *li = context->chain.next;
    while (li != head) {
        first_queue = context->q->next;
        queue_contex_t *li_context = list_entry(li, queue_contex_t, chain);
        li_context->q->prev->next = NULL;
        struct list_head *curr = li_context->q->next;
        struct list_head **ptr = &context->q->next;
        for (;;) {
            if (!curr) {
                *ptr = first_queue;
                break;
            }
            if (!first_queue) {
                *ptr = curr;
                break;
            }
            if (strcmp(list_entry(first_queue, element_t, list)->value,
                       list_entry(curr, element_t, list)->value) > 0) {
                if (descend) {
                    *ptr = first_queue;
                    ptr = &first_queue->next;
                    first_queue = first_queue->next;
                } else {
                    *ptr = curr;
                    ptr = &curr->next;
                    curr = curr->next;
                }
            } else {
                if (descend) {
                    *ptr = curr;
                    ptr = &curr->next;
                    curr = curr->next;
                } else {
                    *ptr = first_queue;
                    ptr = &first_queue->next;
                    first_queue = first_queue->next;
                }
            }
        }
        context->size += li_context->size;
        INIT_LIST_HEAD(li_context->q);
        li_context->size = 0;
        li = li->next;
    }
    struct list_head *index = context->q, *next = index->next;
    while (next) {
        next->prev = index;
        index = index->next;
        next = index->next;
    }
    context->q->prev = index;
    index->next = context->q;
    return context->size;
}

研讀 Linux 核心原始程式碼的 lib/list_sort.c

此方法是使用 buttom up merge sort,並且沒有使用遞迴