# 2020q1 Homework1 (lab0)
contributed by < `mistysya` >
題目出處: [lab0](https://hackmd.io/@sysprog/linux2020-lab0)
## 開發環境
```shell
$ lsb_release -a
Description: Ubuntu 18.04.4 LTS
$ gcc 7.4.0
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
```
## queue.c 實作
### q_new()
建立一個新的空佇列 (empty queue)
```cpp
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q == NULL) {
printf("ERROR::q_new()::Allocate memory to queue failed.\n");
return NULL;
}
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
### q_free()
清除該條佇列,釋放佇列占用的記憶體,同時要一併釋放佇列內元素占用的記憶體
不能僅單純釋放 queue 的元素,還需要釋放 queue 元素的字串占用的記憶體,因為當初插入 queue 時,有特別為字串 allocate memory ,這部分的記憶體並不會自動釋放。
```c=
void q_free(queue_t *q)
{
if (q == NULL)
return;
list_ele_t *tmp = q->head;
while (tmp) {
q->head = tmp->next;
free(tmp->value);
free(tmp);
tmp = q->head;
}
free(q);
}
```
### q_insert_head()
自佇列的開頭 (head) 插入新元素,同時需將字串 (string) 複製一份給新元素
**實作遇到問題 :**
* 在進行 make test 時,部分測試都會顯示執行 `q_free()` 之後仍有元素存在。
* 當初實作 function 時雖然有想到進行字串複製可能出現 allocate memory 失敗的情況 (14行),做了錯誤訊息顯示以及結束 function 的處理。
但沒有考慮到在之前 (8行) 已經為新插入元素 allocate memory 成功,如果直接結束 function 會發生 **memory leak** 的情況。
因此補上 `free(newh)` 之後 (17行) 便解決該問題。
```c=
bool q_insert_head(queue_t *q, char *s)
{
if (q == NULL) {
printf("ERROR::q_insert_head()::Pointer to queue is NULL.\n");
return false;
}
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if (newh == NULL) {
printf("ERROR::q_insert_head()::Allocate memory to newh failed.\n");
return false;
}
int length = strlen(s);
char *value = malloc(sizeof(char) * (length + 1));
if (value == NULL) {
printf("ERROR::q_insert_head()::Allocate memory to value failed.\n");
free(newh);
return false;
}
strncpy(value, s, length);
value[length] = '\0';
newh->next = q->head;
newh->value = value;
q->head = newh;
if (q->tail == NULL)
q->tail = newh;
q->size += 1;
return true;
}
```
### q_insert_tail()
自佇列的尾端 (tail) 插入新元素,同時需將字串 (string) 複製一份給新元素
為了達到在 $O(1)$ 時間複雜度內完成插入,額外新增一個指標來記錄 tail 的位置
```c=
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size;
} queue_t;
```
**實作遇到問題 :**
* 問題同 `q_insert_head()` ,在進行 make test 時,部分測試都會顯示執行 `q_free()` 之後仍有元素存在。
以同樣的方式解決。
```c=
bool q_insert_tail(queue_t *q, char *s)
{
if (q == NULL) {
printf("ERROR::q_insert_tail()::Pointer to queue is NULL.\n");
return false;
}
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if (newh == NULL) {
printf("ERROR::q_insert_tail()::Allocate memory to newh failed.\n");
return false;
}
int length = strlen(s);
char *value = malloc(sizeof(char) * (length + 1));
if (value == NULL) {
printf("ERROR::q_insert_tail()::Allocate memory to value failed.\n");
free(newh);
return false;
}
strncpy(value, s, length);
value[length] = '\0';
newh->next = NULL;
newh->value = value;
if (q->tail) {
q->tail->next = newh;
} else {
q->head = newh;
}
q->tail = newh;
q->size += 1;
return true;
}
```
### q_remove_head()
自佇列的開頭 (head) 移除元素,同時將移除元素的字串內容複製到指定字串中
需要特別注意元素的字串長度是否超過指定的 buffer size,否則在執行 `strncpy` 時可能會出錯,同時也需要在字串尾部補上 `\0`,因為 `strncpy` 不會自動在目標字串補上空字元。
```c=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (q == NULL) {
printf("ERROR::q_remove_head()::Pointer to queue is NULL.\n");
return false;
}
if (q->head == NULL) {
printf("ERROR::q_remove_head()::Queue is empty.\n");
return false;
}
if (sp == NULL) {
printf("ERROR::q_remove_head()::Pointer to string is NULL.\n");
return false;
}
list_ele_t *tmp = q->head;
int length = strlen(tmp->value);
if (length < bufsize) {
strncpy(sp, tmp->value, length);
sp[length] = '\0';
} else {
strncpy(sp, tmp->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
if (q->tail == q->head)
q->tail = NULL;
q->head = q->head->next;
free(tmp->value);
free(tmp);
q->size -= 1;
return true;
}
```
### q_size()
回傳佇列中元素的數量
為了達到在 $O(1)$ 時間複雜度內完成插入,額外新增一個整數變數來記錄元素的數量
```c=
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size;
} queue_t;
```
```c=
int q_size(queue_t *q)
{
if (q == NULL || q->head == NULL)
return 0;
return q->size;
}
```
### q_reverse()
在不額外配置或釋放任何元素的情況下,反向排序整條 queue
```c=
void q_reverse(queue_t *q)
{
if (q == NULL || q->head == NULL || q->head->next == NULL)
return;
q->tail = q->head;
list_ele_t *prevEle = NULL;
list_ele_t *nextEle = q->head->next;
while (nextEle) {
q->head->next = prevEle;
prevEle = q->head;
q->head = nextEle;
nextEle = nextEle->next;
}
q->head->next = prevEle;
}
```
### q_sort()
以遞增順序排序 queue 中的元素
**實作遇到問題 :**
* 使用 selection sort 或 insertion sort 都會出現超時的問題
* 改用 **merge sort** 解決超時問題,但部分測資會出現遞迴層數過多導致 **stack overflow** 的問題
* 遞迴層數過多造成的 stack overflow 問題,透過改用迭代的方式進行 merge sort 解決
```c=
int min(int x, int y)
{
return x < y ? x : y;
}
void q_sort(queue_t *q)
{
if (q == NULL)
return;
int length = q->size;
for (int width = 1; width < length; width *= 2) {
list_ele_t tmp_head;
list_ele_t *tmp = &tmp_head;
list_ele_t *l_node = q->head;
list_ele_t *r_node = q->head;
for (int start = 0; start < length; start += width * 2) {
int mid = min(start + width, length),
end = min(start + width * 2, length);
int ls = start;
int le = mid;
int rs = mid;
int re = end;
for (int i = 0; i < width; i++) {
if (r_node->next)
r_node = r_node->next;
else
break;
}
while (ls < le && rs < re) {
if (strnatcmp(l_node->value, r_node->value) < 0) {
tmp->next = l_node;
tmp = tmp->next;
l_node = l_node->next;
tmp->next = NULL;
ls += 1;
} else {
tmp->next = r_node;
tmp = tmp->next;
r_node = r_node->next;
tmp->next = NULL;
rs += 1;
}
}
while (ls < le) {
tmp->next = l_node;
tmp = tmp->next;
l_node = l_node->next;
tmp->next = NULL;
ls += 1;
}
while (rs < re) {
tmp->next = r_node;
tmp = tmp->next;
r_node = r_node->next;
tmp->next = NULL;
rs += 1;
}
l_node = r_node;
}
q->head = tmp_head.next;
}
}
```