# 2020q1 Homework1 (lab0)
contributed by < [`BWbwchen`](https://github.com/BWbwchen/lab0-c) >
## 開發環境
```shell
$ uname -a
Linux bw-pc 4.19.102-1-MANJARO #1 SMP Wed Feb 5 19:48:44 UTC 2020 x86_64 GNU/Linux
$ gcc --version
gcc (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
```
## 作業要求
* `q_new`: 建立新的「空」佇列;
* `q_free`: 釋放佇列所佔用的記憶體;
* `q_insert_head`: 在佇列開頭 (head) 加入 (insert) 給定的新元素 (以 LIFO 準則);
* `q_insert_tail`: 在佇列尾端 (tail) 加入 (insert) 給定的新元素 (以 FIFO 準則);
* `q_remove_head`: 在佇列開頭 (head) 移除 (remove) 給定的元素。
* `q_size`: 計算佇列中的元素數量。
* `q_reverse`: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列元素,換言之,它只能重排已經存在的元素;
* `q_sort`: 以==遞增順序==來排序鏈結串列的元素
## 實作
### Structure
* 為了要讓`q_insert_tail` 和 `q_size` 可以為 $O(1)$ ,因此加入新的 struct member 來隨時紀錄 `tail` 和 `size`
```cpp=
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size;
} queue_t;
```
### q_new
* malloc 一個新的記憶體空間,但須注意 malloc 有沒有失敗,並做初始化
```cpp=
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q == NULL)
return NULL;
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
### q_free
* 單純的從 head 一路 free 到結尾,但須注意 `value` 也是經由 malloc 取得的,因此也要 free
```cpp=
void q_free(queue_t *q)
{
if (q == NULL)
return;
while (q->head) {
list_ele_t *tmp = q->head;
q->head = q->head->next;
free(tmp->value);
free(tmp);
}
free(q);
}
```
### q_insert_head
* 需注意如果原先的 queue 是空的情況,還有在 malloc value 失敗時,要記得把原本的 newh 還回去
```cpp=
bool q_insert_head(queue_t *q, char *s)
{
if (q == NULL)
return false;
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if (newh == NULL)
return false;
newh->value = malloc((strlen(s) + 1) * sizeof(char));
if (newh->value == NULL) {
free(newh);
return false;
}
strncpy(newh->value, s, strlen(s));
newh->value[strlen(s)] = '\0';
if (q->head == NULL) {
q->tail = newh;
}
newh->next = q->head;
q->head = newh;
q->size++;
return true;
}
```
### q_insert_tail
* 需要注意 queue 為空的狀況,要記得更新 queue 的資料
```cpp=
bool q_insert_tail(queue_t *q, char *s)
{
if (q == NULL)
return false;
list_ele_t *tmp;
tmp = malloc(sizeof(list_ele_t));
if (tmp == NULL)
return false;
tmp->next = NULL;
tmp->value = malloc((strlen(s) + 1) * sizeof(char));
if (tmp->value == NULL && strlen(s) != 0) {
free(tmp);
return false;
}
strncpy(tmp->value, s, strlen(s));
tmp->value[strlen(s)] = '\0';
if (q->tail == NULL) {
q->head = tmp;
} else {
q->tail->next = tmp;
}
q->tail = tmp;
q->size++;
return true;
}
```
### q_remove_head
* 需要注意如果 queue 的長度為 1 的狀況。也要記得刪除的 node 需要把 value free 掉
```cpp=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (q == NULL || q->head == NULL)
return false;
if (sp != NULL) {
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
if (q->head->next == NULL) {
free(q->head->value);
free(q->head);
q->head = NULL;
q->tail = NULL;
q->size = 0;
return true;
}
list_ele_t *tmp;
tmp = q->head;
q->head = q->head->next;
q->size--;
free(tmp->value);
free(tmp);
return true;
}
```
### q_size
```cpp=
int q_size(queue_t *q)
{
return !q ? 0 : q->size;
}
```
### q_reverse
* 因為 list_ele_t 只記住 next 的資料,因此需要一個 pointer 來記住下一個 element ,才能順利將 next 的資料更新。
```cpp=
void q_reverse(queue_t *q)
{
if (q == NULL || q->head == NULL || q->size == 1)
return;
list_ele_t *pre = NULL, *now = q->head, *after = q->head->next;
q->tail = q->head;
while (after) {
now->next = pre;
pre = now;
now = after;
after = after->next;
}
now->next = pre;
q->head = now;
}
```
### q_sort
* 我採用 merge sort 的方式
* 如果採用測驗 1 的方式去切 list 的話,複雜度沒辦法壓下來,因此要找到 list 區段的中心才能將複雜度壓到 $O(nlogn)$
```cpp=
list_ele_t *merge_sort(list_ele_t *start)
{
if (start == NULL || start->next == NULL)
return start;
list_ele_t *mid;
mid = get_mid(start);
list_ele_t *right = mid->next;
mid->next = NULL;
return merge_list(merge_sort(start), merge_sort(right));
}
```
* get_mid()
* 用兩個走訪速率為 1 : 2 的 pointer 去走訪到中間點
```cpp=
list_ele_t *get_mid(list_ele_t *start)
{
if (start == NULL)
return start;
list_ele_t *slow = start, *fast = start;
while (fast->next != NULL && fast->next->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
```
* merge_list
* 這邊我就直接採用測驗題的方式去做 merge
```cpp=
list_ele_t *merge_list(list_ele_t *left, list_ele_t *right)
{
// Merge
list_ele_t *start = NULL;
for (list_ele_t *merge = NULL; left || right;) {
if (!right || (left && strcmp(left->value, right->value) < 0)) {
if (!merge) {
start = merge = left;
} else {
merge->next = left;
merge = merge->next;
}
left = left->next;
} else {
if (!merge) {
start = merge = right;
} else {
merge->next = right;
merge = merge->next;
}
right = right->next;
}
}
return start;
}
```