# 2020q1 Homework1 (lab0)
contributed by < `kylekylehaha` >
###### tags: `linux2020`
## 開發環境
```shell
$ uname -a
Linux kyleD 5.3.0-40-generic
$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
```
---
## 實做 queue 需求
利用 singly-linked list 來實做 FIFO & LIFO queue,並滿足下列功能:
- q_new: 建立新的「空」佇列
- q_free: 釋放 queue 所佔用的記憶體,其中包含 header & 所有 list elements
- q_insert_head: 在 head 加入 element,並採用LIFO準則
- q_insert_tail: 在 tail 加入 element,並採用FIFO準則
- q_remove_head: 從 head 移除一 element
- q_size: 計算 queue 中的 element 個數
- q_reverse: 將 queue 反轉
- q_sort: 利用 bubble sort 的方式,將 queue 做遞增排列
### q_new
平常沒有習慣檢查 malloc 是否成功,經過這次作業開始養成這種好習慣。
```cpp
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (!q)
return 0;
q->size = 0;
q->head = NULL;
q->tail = NULL;
return q;
}
```
### q_free
free 的時候記得要先 free 裡面的資料後,再 free 整個 element。
```cpp
void q_free(queue_t *q)
{
if (!q)
return;
list_ele_t *current_ptr = q->head;
while (current_ptr) {
list_ele_t *tmp = current_ptr;
current_ptr = current_ptr->next;
free(tmp->value);
free(tmp);
}
free(q);
return;
}
```
### q_insert_head
須注意的地方仍和前面一樣,每當 malloc 時,都要檢查是否 malloc 成功。
```cpp
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
int len = strlen(s);
if (!q)
return false;
newh = malloc(sizeof(list_ele_t));
if (!newh)
return false;
newh->value = malloc(sizeof(char) * len + 1);
if (!newh->value) {
free(newh);
return false;
}
memset(newh->value, 0, len);
strncpy(newh->value, s, len);
newh->value[len] = '\0';
// Maintain queue structure
newh->next = q->head;
q->head = newh;
if (q->size == 0) {
q->tail = newh;
}
q->size += 1;
return true;
}
```
### q_insert_tail
概念和 q_insert_head 相同,兩者不同只在於 maintain queue structure 的部份。
```cpp
bool q_insert_tail(queue_t *q, char *s)
{
list_ele_t *newh;
int len = strlen(s);
if (!q)
return false;
newh = malloc(sizeof(list_ele_t));
if (!newh)
return false;
newh->value = malloc(sizeof(char) * len + 1);
if (!newh->value) {
free(newh);
return false;
}
memset(newh->value, 0, len);
strncpy(newh->value, s, len);
newh->value[len] = '\0';
newh->next = NULL;
if (q->size == 0) {
q->head = newh;
} else {
q->tail->next = newh;
}
q->tail = newh;
q->size += 1;
return true;
}
```
### q_remove_head
目前有bug:
`ERROR: Corruption detected in block with address 0x5616328dad80 when attempting to free it
`
目前發現是由於 free(tmp->value) ,也就是 free list_element 內的字串所產生的error
已解決:後來我發現原來是我在放置 '\0'位置時,忘了 len 已經加一,不再等於 strlen(s)。
```cpp
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || !q->head)
return false;
list_ele_t *tmp = q->head;
int len = strlen(q->head->value);
if (sp) {
memset(sp, 0, bufsize);
strncpy(sp, tmp->value, len);
}
q->head = q->head->next;
free(tmp->value);
free(tmp);
q->size -= 1;
return true;
}
```
### q_size
由於在前面的 q_insert_head, q_insert_tail, q_remove_head 中,當有增加/減少 element 時,就以調整過 q->size,故這裡只須回傳 q->size 值。須注意仍要檢查 q 是否為 NULL。
```cpp
int q_size(queue_t *q)
{
if (!q)
return 0;
return q->size;
}
```
### q_reverse
在這裡,我參考 [Reverse a linked list](https://www.geeksforgeeks.org/reverse-a-linked-list/) 裡的gif,讓我更快速理解 linked list reverse 的機制。
```cpp
void q_reverse(queue_t *q)
{
if (!q)
return;
list_ele_t *next, *curr, *prev;
prev = NULL;
next = NULL;
curr = q->head;
q->tail = q->head;
while (curr) {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
q->head = prev;
}
```
### q_sort
* 採用本週測驗的 merge sort with single linked list。然而須注意的是經過 sort 後,原本為 q->tail 所指的 element 不一定仍為 queue tail,故再重新走一次 queue 來找到真正的 tail。
* Time complexity: O(nlogn + n), n為 q->size。
* 但發現跑不過 trace-15-perf & trace-16-perf
```cpp
list_ele_t *sort(list_ele_t *start)
{
if (!start || !start->next)
return start;
list_ele_t *left = start;
list_ele_t *right = start->next;
left->next = NULL;
left = sort(left);
right = sort(right);
for (list_ele_t *merge = NULL; left || right;) {
if (!right || (left && strnatcmp((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;
}
void q_sort(queue_t *q)
{
if (!q || !q->head)
return;
q->head = sort(q->head);
// Find new q->tail after sort
while (q->tail->next) {
q->tail = q->tail->next;
}
}
```
## 開發問題
* problem1
利用 Valgrind 去分析 trace-08-robust.cmd,發現在做 q_sor t時沒有檢查到 empty queue 的 case。

```cpp
void q_sort(queue_t *q)
{
- if (!q)
+ if (!q || !q->head)
return;
q->head = sort(q->head);
// Find new q->tail after sort
while (q->tail->next) {
q->tail = q->tail->next;
}
}
```
* problem2
由於課堂練習的 merge sort 沒有從中間開始切,導致整體時間太長,跑不過trace-16,故必須製作 split,從中間開始切,才能縮短整體時間。
利用slow & fast,速度 1:2 來找尋中間值。
```cpp
void split(list_ele_t *source, list_ele_t **left, list_ele_t **right)
{
if (!source || !source->next) {
*left = source;
*right = NULL;
return;
}
list_ele_t *slow = source;
list_ele_t *fast = source;
while (fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
*left = source;
*right = slow->next;
slow->next = NULL;
return;
}
```
## 論文研讀 Dude, is my code constant time?
## 解釋 select 系統呼叫在本程式的使用方式
## Reference
- [Queue: Intro(簡介),並以linked list 實做](https://alrightchiu.github.io/SecondRound/queue-introjian-jie-bing-yi-linked-listshi-zuo.html)
- [Reverse a linked list](https://www.geeksforgeeks.org/reverse-a-linked-list/)