# 2025q1 Homework1 (lab0)
contributed by <markarz>
{%hackmd NrmQUGbRQWemgwPfhzXj6g %}
## 開發環境
```
jorge@jorge-MS-7E56:~/linux2025/lab0-c$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 48 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 16
On-line CPU(s) list: 0-15
Vendor ID: AuthenticAMD
Model name: AMD Ryzen 7 9700X 8-Core Processor
CPU family: 26
Model: 68
Thread(s) per core: 2
Core(s) per socket: 8
Socket(s): 1
Stepping: 0
Frequency boost: enabled
CPU(s) scaling MHz: 53%
CPU max MHz: 5581.0000
CPU min MHz: 600.0000
BogoMIPS: 7599.99
jorge@jorge-MS-7E56:~/linux2025/lab0-c$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)
```
## 實做queue.c
### q_new
使用malloc配置給head一個struct list_head的記憶體空間大小,如果空見配置成功就會用list.h裡的INIT_LIST_HEAD()初始化head。
```
struct list_head *q_new()
{
struct list_head *head = malloc(sizeof(struct list_head));
if (head)
INIT_LIST_HEAD(head);
return head;
}
```
---
### q_free
如果l指向null,就不做任何事,如果指向非空會用list.h裡面的list_for_each_entry_safe找尋每個節點,並用 q_release_element釋放空間,最後在釋放l的記憶體空間。
```
void q_free(struct list_head *l)
{
if (!l)
return;
element_t *entry, *safe = NULL;
list_for_each_entry_safe (entry, safe, l, list)
q_release_element(entry);
free(l);
}
```
### q_insert_head
先驗證 head 和 s 是否為空指標,嘗試使用 malloc 動態分配 element_t 結構所需的記憶體空間給 new_element;若記憶體分配失敗,則立即返回 false。若成功分配,則進一步利用 strdup() 函式對字串 s 進行深度複製,將內容安全地賦值給 new_element->value。若字串複製失敗,則需釋放已分配的 new_element 記憶體並返回 false。最後,在所有前置條件和記憶體操作皆成功的情況下,透過 list_add 將新建立的節點插入至鏈結串列的頭部。
```
bool q_insert_head(struct list_head *head, char *s)
{
if (!head || !s) // 確保 head 和 s 不是 NULL
return false;
element_t *new_element = malloc(sizeof(element_t));
if (!new_element) // 檢查 malloc 是否成功
return false;
new_element->value = strdup(s); // 分配記憶體並複製字串
if (!new_element->value) {
free(new_element);
return false;
}
list_add(&new_element->list, head); // 插入節點
return true;
}
```
### q_insert_tail
先驗證 head 和 s 是否為空指標,嘗試使用 malloc 動態分配 element_t 結構所需的記憶體空間給 new_element;若記憶體分配失敗,則立即返回 false。若成功分配,則進一步利用 strdup() 函式對字串 s 進行深度複製,將內容安全地賦值給 new_element->value。若字串複製失敗,則需釋放已分配的 new_element 記憶體並返回 false。最後,在所有前置條件和記憶體操作皆成功的情況下,透過 list_add_tail 將新建立的節點插入至鏈結串列的尾巴。
```
bool q_insert_tail(struct list_head *head, char *s)
{
if (!head || !s) // 確保 head 和 s 不是 NULL
return false;
element_t *new_element = malloc(sizeof(element_t));
if (!new_element) // 檢查 malloc 是否成功
return false;
new_element->value = strdup(s); // 分配記憶體並複製字串
if (!new_element->value) {
free(new_element);
return false;
}
list_add_tail(&new_element->list, head); // 插入節點
return true;
}
```
### q_remove_head
先驗證head 和 s 是否為空指標,接下來用list_first_entry()將elem指向queue的第一個節點,用list_del()將elem變成一個獨立的節點,用strncpy()將elem->value的字串複製到sp也就是緩衝區位置,最後回傳elem的指標。
```
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *elem = list_first_entry(head, element_t, list);
list_del(&elem->list);
if (sp && bufsize > 0) {
sp[0] = '\0';
if (elem->value) {
strncpy(sp, elem->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
}
return elem;
}
```
### q_remove_tail
先驗證head 和 s 是否為空指標,接下來用list_last_entry()將elem指向queue最後一個節點,用list_del()將elem變成一個獨立的節點,用strncpy()將elem->value的字串複製到sp也就是緩衝區位置,最後回傳elem的指標。
```
element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *elem = list_last_entry(head, element_t, list);
list_del(&elem->list);
if (sp && bufsize > 0) {
// 先將緩衝區置空,避免後面狀況導致舊資料殘留
sp[0] = '\0';
// 若 elem->value 不為 NULL,就做字串拷貝
if (elem->value) {
strncpy(sp, elem->value, bufsize - 1);
sp[bufsize - 1] = '\0'; // 確保結尾有 '\0'
}
}
return elem;
}
```
### q_delete_mid
```
bool q_delete_mid(struct list_head *head)
{
if (!head)
return NULL;
if (list_is_singular(head)) {
list_del(head);
return true;
}
struct list_head *cur = NULL;
struct list_head *tmp = NULL;
int mid = 0, current = -1;
cur = head;
mid = q_size(head) / 2;
while (cur != NULL && current != mid) {
tmp = cur;
cur = cur->next;
current++;
}
element_t *del_element = list_entry(cur, element_t, list);
tmp->next = cur->next;
q_release_element(del_element);
return true;
}
```
### q_delete_dup
```
bool q_delete_dup(struct list_head *head)
{
if (!head || list_empty(head) || list_is_singular(head))
return false;
struct list_head *cur = head->next;
while (cur != head) {
element_t *elem1 = list_entry(cur, element_t, list);
struct list_head *next = cur->next;
bool has_duplicate = false;
// 檢查後面是否有相同的數據
while (next != head) {
element_t *elem2 = list_entry(next, element_t, list);
if (strcmp(elem1->value, elem2->value) == 0) {
struct list_head *dup = next;
next = next->next;
list_del(dup);
q_release_element(elem2);
has_duplicate = true;
} else
break;
}
struct list_head *temp = cur;
cur = next;
// 如果 `has_duplicate == true`,代表 `temp` 也應該刪除
if (has_duplicate) {
list_del(temp);
q_release_element(elem1);
}
}
return true;
}
```
### q_swap
```
void q_swap(struct list_head *head)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
struct list_head *cur = head->next;
while (cur != head && cur->next != head) {
struct list_head *next = cur->next;
// 取得 element_t 結構體
element_t *elem1 = list_entry(cur, element_t, list);
element_t *elem2 = list_entry(next, element_t, list);
// 交換 `value`(這裡只交換指標,因為 `value` 是字串)
char *temp = elem1->value;
elem1->value = elem2->value;
elem2->value = temp;
// 移動 cur 到下一對
cur = next->next;
}
}
```
### q_reverse
```
void q_reverse(struct list_head *head)
{
if (!head || list_empty(head) || list_is_singular(head))
return;
struct list_head *cur = head->next, *last = head->prev;
while (cur != last && last->next != cur) {
element_t *elem1 = list_entry(cur, element_t, list);
element_t *elem2 = list_entry(last, element_t, list);
// 交換 `value`
char *temp = elem1->value;
elem1->value = elem2->value;
elem2->value = temp;
cur = cur->next;
last = last->prev;
}
}
```
### q_reverseK
```
void q_reverseK(struct list_head *head, int k)
{
if (!head || list_empty(head) || list_is_singular(head) || k == 1)
return;
struct list_head *cur = head->next;
while (cur != head) {
struct list_head *start = cur, *end = cur;
int count = 1;
// 找到 k 個節點
while (count < k && end->next != head) {
end = end->next;
count++;
}
if (count < k) // 剩餘節點不足 k 個,停止
break;
struct list_head *left = start, *right = end;
while (left != right && right->next != left) {
element_t *elem1 = list_entry(left, element_t, list);
element_t *elem2 = list_entry(right, element_t, list);
// 交換 `value`
char *temp = elem1->value;
elem1->value = elem2->value;
elem2->value = temp;
left = left->next;
right = right->prev;
}
cur = end->next; // 移動到下一組
}
}
```
我一開始用 快速排序(Quick Sort) 的方式寫,結果測資過不了。測資會過不了的原因,可能是因為其中含有已排列過的資料,所以我後來改用 合併排序(Merge Sort)。改用之後,時間複雜度基本上都可以維持在 O(nlogn)。
[merge sort參考連結](https://hackmd.io/@lambert-wu/list-merge-sort)
### q_sort
```
static inline void split_in_half(struct list_head *src,struct list_head *left_out)
{
struct list_head *slow_ptr =
src->next; // `slow_ptr` will be the tail of the left part.
// Starts at the first actual element.
struct list_head *fast_ptr =
src->next->next;
while (fast_ptr != src && fast_ptr->next != src) {
slow_ptr = slow_ptr->next;
fast_ptr = fast_ptr->next->next;
}
list_cut_position(left_out, src, slow_ptr);
}
static inline void merge_two_sorted(struct list_head *dest,
struct list_head *left,
struct list_head *right,
bool descend)
{
while (!list_empty(left) && !list_empty(right)) {
element_t *a = list_first_entry(left, element_t, list);
element_t *b = list_first_entry(right, element_t, list);
int cmp = strcmp(a->value, b->value);
if (descend) { // If descending, reverse comparison logic
cmp = -cmp;
}
if (cmp <= 0) {
list_move_tail(&a->list, dest);
} else {
list_move_tail(&b->list, dest);
}
}
if (!list_empty(left)) {
list_splice_tail_init(left, dest);
}
if (!list_empty(right)) {
list_splice_tail_init(right, dest);
}
}
void q_sort(struct list_head *head, bool descend)
{
if (!head || list_empty(head) || list_is_singular(head)) {
return;
}
split_in_half(head, &left_half);
q_sort(&left_half, descend); // Sort the left part.
q_sort(head, descend); // Sort the right part (which is now in `head`).
struct list_head merged;
INIT_LIST_HEAD(&merged);
merge_two_sorted(&merged, &left_half, head, descend);
list_splice_tail_init(&merged, head);
}
```
**自己寫的quick sort與參考別人寫的merge sort測資比較**