# 2020q3 Homework1 (lab0)
contributed by < `chou0513` >
## Outline
## 環境
```shell
$ uname -a
Linux ubuntu-VirtualBox 5.4.0-47-generic #51-Ubuntu SMP Fri Sep 4 19:50:52 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```
## 作業要求
在 ``queue.[c/h]`` 中完成以下實作
* `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`: 以==遞增順序==來排序鏈結串列的元素
## 實作流程
### queue.h
* 更改 `queue.h` 裡面 `queue_t` ,使得 `q_size()` 和 `q_insert_tail()` 有辦法在 $O(1)$ 的時間完成
* 依照作業教學,將 `queue.h` 中的 `queue_t` 增加成員 (`size`)
* 此舉能讓 `q_size()` 藉由直接讀取 `queue_t` 中的 `size`,不必每次重新計算 queue 的大小
* 缺點是必須在每次讓 queue 新增或刪除成員時,管理 queue 的 size 以及 `tail` 指標,也讓 queue 所需的記憶體空間增大 `sizeof(pointer) + sizeof(int)`
```c
/* Queue structure */
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size; /* Size of queue */
} queue_t
```
### queue.h
* **q_new**
* 初始化 queue_t 前,確認是否成功分配記憶體,避免操作到空的指標
```c
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q) {
q->head = NULL;
q->tail = NULL;
q->size = 0;
}
return q;
}
```
* **q_free**
* 新增 tmp 作為釋放用的節點
* 藉由 q->head 的移動,遍歷整個 queue,並一一清除
```c
void q_free(queue_t *q)
{
if (!q)
return;
list_ele_t *tmp = q->head;
while (q->head) {
q->head = q->head->next;
tmp->next = NULL;
free(tmp->value);
free(tmp);
tmp = q->head;
}
free(q);
}
```
* **q_insert_head**
* 將新元素 (list_ele_t) 放入 queue 的前端
* 先判斷傳入的 q 是否為 NULL,避免分配記憶體後才發現 q 為 NULL,造成 memory leak
* 在分配記憶體給新的元素後,判斷是否分配成功,若失敗,則將之前分配的 newh 清除
* 以 C library <string.h> 的 strncpy 複製 s,放入新元素的 value
* 因為 strncpy 不會自動將其他位置設為 \0,所以使用 memset 將 newh->value 歸零
* 確保 q->tail 能夠正常運作,第一個 element 建立時,將 q->tail 指向 q->head,且隨著新增元素,位移到最後
```c
bool q_insert_head(queue_t *q, char *s)
{
if (!q)
return false;
// Check separately to avoid extra malloc cause memory leak
list_ele_t *newh; // newh means new element in head
newh = malloc(sizeof(list_ele_t));
if (!newh) {
return false;
}
// Allocate space and copy the string
newh->value = malloc(sizeof(char) * (strlen(s) + 1));
if (!newh->value) {
free(newh);
return false;
}
memset(newh->value, '\0', strlen(s) + 1);
strncpy(newh->value, s, strlen(s));
newh->next = q->head;
q->head = newh;
// first time
if (!q->tail) {
q->tail = q->head;
} else {
while (q->tail->next) {
q->tail = q->tail->next;
}
}
q->size += 1;
return true;
}
```
* **q_insert_tail**
* 與 q_insert_head 差不多
* 將新元素 (list_ele_t) 放入 queue 的尾端
* 若 queue 為空 (q->tail == NULL),則將 head 和 tail 同時指向新元素 (newt)
```c
bool q_insert_tail(queue_t *q, char *s)
{
if (!q)
return false;
// Check separately to avoid extra malloc cause memory leak
list_ele_t *newt; // newt means new element in tail
newt = malloc(sizeof(list_ele_t));
if (!newt)
return false;
newt->value = malloc(sizeof(char) * strlen(s) + 1);
if (!newt->value) {
free(newt);
return false;
}
memset(newt->value, '\0', strlen(s) + 1);
strncpy(newt->value, s, strlen(s));
newt->next = NULL;
if (!q->tail) {
q->tail = q->head = newt;
} else {
q->tail->next = newt;
q->tail = newt;
}
q->size += 1;
return true;
}
```
* **q_remove_head**
* **q_size**
* **q_reverse**
* **q_sort**