# 2020q1 Homework1 (lab0)
contributed by < `Hsuhaoxiang` >
## 實驗環境
```shell
$ uname -a
Linux haoxiang-System-Product-Name 5.3.0-40-generic #32~18.04.1-Ubuntu SMP Mon Feb 3 14:05:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
```
## 開發過程
### q_size
* 為了在 $O(1)$ 完成這項操作所以在 queue_t 增加 size
* 如果 q 為 NULL 必須回傳 0
```cpp
int q_size(queue_t *q)
{
if (!q)
return 0;
return q->size;
}
```
### q_new
* 建立新的「空」佇列
* 要注意的是 malloc 使否成功分配記憶體位置
* 初始化 queue_t 中的變數 tail , head , size
```cpp
queue_t *q_new()
{
queue_t *q = (queue_t *) malloc(sizeof(queue_t));
if (!q)
return NULL;
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
### q_insert_head
* 在佇列開頭 (head) 加入 (insert) 給定的新元素 (以 LIFO 準則)
* 首先若 q 為NULL則直接 retrun false ,每次 malloc 也需要確認記憶體
是否分配成功,而在分配 value 的記憶體位置時,如果失敗要先回收
newh 的記憶體,否會造成沒有指標指向該記憶體。
* 使用 strcpy 會跳出 Dangerous function detected 警告無法 commit
所以改用 strncpy
* 當 queue 為空時應該要將 q->tail 指向新增的 newh
```cpp
bool q_insert_head(queue_t *q, char *s)
{
int Strlen = strlen(s);
if (!q)
return false;
list_ele_t *newh;
newh = (list_ele_t *) malloc(sizeof(list_ele_t));
if (!newh)
return false;
newh->value = (char *) malloc((Strlen + 1) * sizeof(char));
if (!(newh->value)) {
free(newh);
return false;
}
strncpy(newh->value, s, Strlen + 1);
newh->next = q->head;
q->head = newh;
q->size += 1;
if (q->size == 1)
q->tail = newh;
return true;
}
```
### q_insert_tail
* 在佇列尾端 (tail) 加入 (insert) 給定的新元素 (以 FIFO 準則)
* 此段程式碼與 `q_insert_head` 非常相似,不同的地方在於要將插入前的 `tail` 的 `next` 指向正要插入的這個元素
```cpp
bool q_insert_tail(queue_t *q, char *s)
{
int Strlen = strlen(s);
if (!q)
return false;
list_ele_t *newh;
newh = (list_ele_t *) malloc(sizeof(list_ele_t));
if (!newh)
return false;
newh->value = (char *) malloc((Strlen + 1) * sizeof(char));
if (!(newh->value)) {
free(newh);
return false;
}
strncpy(newh->value, s, Strlen + 1);
newh->next = NULL;
q->size += 1;
if (q->size == 1)
q->head = newh;
else
q->tail->next = newh;
q->tail = newh;
return true;
}
```
### q_remove_head
* 在佇列開頭 (head) 移除 (remove) 給定的元素。
* 將 node 中的值複製到 sp 中
* 把 `head` 指向新的頭也就是 `head->next`
* `size` 減一,並將此元素所用到的記憶體空間釋放
```cpp
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || q->size == 0)
return false;
if (sp) {
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
if (q->size == 1) {
free(q->head->value);
free(q->head);
q->head = NULL;
q->size -= 1;
return true;
}
list_ele_t *tmp;
tmp = q->head;
q->head = q->head->next;
q->size -= 1;
free(tmp->value);
free(tmp);
if (q->size == 0)
q->tail = NULL;
return true;
}
```
### q_reverse
* 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列元素,換言之,它只能重排已經存在的元素
* 如果 `queue` 的 size 為 0 或 1 直接結束
* 否則一一將元素的連結反轉
```cpp
void q_reverse(queue_t *q)
{
if (!q || q->size == 0) {
return;
}
if (q->size == 1) {
return;
}
list_ele_t *pre, *cur, *pos;
pre = pos = NULL;
cur = q->head;
while (cur) {
pos = cur->next;
cur->next = pre;
pre = cur;
cur = pos;
}
cur = q->head;
q->head = q->tail;
q->tail = cur;
}
```
### q_free
* 釋放佇列所佔用的記憶體
* 使用 while 走訪所有元素並且釋放記憶體
```cpp
void q_free(queue_t *q)
{
if (!q) {
return;
}
if (q->head) {
list_ele_t *tmp;
while (q->head) {
tmp = q->head;
q->head = tmp->next;
free(tmp->value);
free(tmp);
}
}
free(q);
}
```
### q_sort
* 以遞增順序來排序鏈結串列的元素
* 使用 merge sort 才能在時間內完成排序
* merge sort 參考[第一週隨堂測驗](https://hackmd.io/@sysprog/linux2020-quiz1)
```cpp
void q_sort(queue_t *q)
{
if (!q || !q->head) {
return;
}
if (q->head == q->tail) {
return;
}
q->head = merge_sort(q->head);
list_ele_t *iter;
iter= q->head;
while (iter && iter->next)
iter = iter->next;
q->tail = iter;
}
```
### merge_sort
```cpp
list_ele_t *merge_sort(list_ele_t *head)
{
if (!head || !head->next) {
return head;
}
list_ele_t *fast = head->next;
list_ele_t *slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
list_ele_t *left = head;
list_ele_t *right = slow->next;
slow->next = NULL;
left = merge_sort(left);
right = merge_sort(right);
return merge(left, right);
}
```
:::danger
考慮以下改寫:
```cpp
static list_ele_t *merge_sort(list_ele_t *head)
{
if (!head || !head->next)
return head;
list_ele_t *slow = head, *fast;
for (fast = head->next; fast && fast->next; fast = fast->next->next)
slow = slow->next;
list_ele_t *mid = slow->next;
slow->next = NULL;
return merge(merge_sort(head), merge_sort(mid));
}
```
要點:
1. 更精簡的程式碼;
2. 善用 for 迴圈;
3. 更精準的變數命名;
4. 宣告加上 `static`,提示編譯器施加更多最佳化;
:notes: jserv
:::
### merge
```cpp
list_ele_t *merge(list_ele_t *left, list_ele_t *right)
{
if (!left) {
return right;
}
if (!right) {
return left;
}
list_ele_t *start = NULL;
for (list_ele_t *merge_ele = NULL; left || right;) {
if (right == NULL || (left && strcmp(left->value, right->value) < 0)) {
if (!merge_ele)
start = merge_ele = left;
else {
merge_ele->next = left;
merge_ele = merge_ele->next;
}
left = left->next;
} else {
if (!merge_ele)
start = merge_ele = right;
else {
merge_ele->next = right;
merge_ele = merge_ele->next;
}
right = right->next;
}
}
return start;
}
```
:::danger
考慮以下改寫:
```cpp
static list_ele_t *merge(list_ele_t *left, list_ele_t *right)
{
list_ele_t *head = NULL;
for (list_ele_t **iter = &head; true; iter = &((*iter)->next)) {
if (!left) {
(*iter) = right;
break;
}
if (!right) {
(*iter) = left;
break;
}
if (strcmp(left->value, right->value) < 0) {
(*iter) = left;
left = left->next;
} else {
(*iter) = right;
right = right->next;
}
}
return head;
}
```
要點:
1. 善用 pointer to pointer 的技巧;
2. 分支的調整,使程式碼更緊湊
:notes: jserv
:::
### test rusult
```
# Test of insert_head and remove_head
--- trace-01-ops 6/6
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, and remove_head
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, and sort
--- trace-04-ops 6/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, and sort
--- trace-05-ops 5/5
+++ TESTING trace trace-06-string:
# Test of truncated strings
--- trace-06-string 6/6
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
--- trace-07-robust 6/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
--- trace-09-robust 6/6
+++ TESTING trace trace-10-malloc:
# Test of malloc failure on new
--- trace-10-malloc 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
--- trace-13-perf 6/6
+++ TESTING trace trace-14-perf:
# Test performance of size
--- trace-14-perf 6/6
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
--- trace-16-perf 6/6
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Probably constant time
Probably constant time
--- trace-17-complexity 5/5
--- TOTAL 100/100
```
### strnatcmp
* 單純把 `strcmp` 改成 `strnatcmp`
```
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
--- trace-01-ops 6/6
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, and remove_head
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, and sort
--- trace-04-ops 6/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, and sort
--- trace-05-ops 5/5
+++ TESTING trace trace-06-string:
# Test of truncated strings
--- trace-06-string 6/6
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
--- trace-07-robust 6/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
--- trace-09-robust 6/6
+++ TESTING trace trace-10-malloc:
# Test of malloc failure on new
--- trace-10-malloc 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
--- trace-13-perf 6/6
+++ TESTING trace trace-14-perf:
# Test performance of size
--- trace-14-perf 6/6
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
--- trace-16-perf 6/6
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Probably constant time
Probably constant time
--- trace-17-complexity 5/5
--- TOTAL 100/100
```