# 2019q1 Homework1(lab0)
contributed by < `110805` >
## 實驗環境
```shell
$ uname -a
Linux bang-HP-Spectre-x360-Convertible-13-w0XX 4.15.0-45-generic #48~16.04.1-Ubuntu SMP Tue Jan 29 18:03:48 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0
```
## 作業要求
本次作業是圍繞在實作 queue 上,且此 queue 是能夠支援 LIFO 及 FIFO 的。
題目要求以下 function 的實作:
- `q_new()` : 建立一個空的 queue
- `q_free()` : 釋放所有被 queue 所使用的記憶體資源
- `q_insert_head()` : 從 queue 的前端插入一個元素 (LIFO)
- `q_insert tail()` : 從 queue 的尾端插入一個元素 (FIFO)
- `q_remove_head()` : 從 queue 的前端刪除一個元素
- `q_size()` : 計算並回傳 queue 中的元素個數
- `q_reverse()` : 反轉 queue 中元素的順序,且此 function 不能 allocate 或是 free 掉任何元素
特別要求:
- 關於每個 function 的特別要求可參照 `queue.c` 及 `queue.h` 中的 comments
- `q_insert_tail()` 及 `q_size()` 這兩個 function 之 time complexity 必須是 $O(1)$
## 實作
- `queue.h`
* `struct queue_t`
為了能夠讓 `q_insert_tail()` 及 `q_size()` 這兩個 function 能夠在 $O(1)$ 的時間完成,在 `struct queue_t` 中增加以下兩個 field: `list_ele_t *tail` `int size`
```clike
typedef struct {
list_ele_t *head; /* Linked list of elements */
/*
You will need to add more fields to this structure
to efficiently implement q_size and q_insert_tail
*/
list_ele_t *tail;
int size;
} queue_t;
```
- `queue.c`
* `q_new()`
建立一個空的 queue ,若無法 allocate 出指定大小的空間,則回傳 NULL。
```clike
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
/* What if malloc returned NULL? */
if(q){
q->head = NULL;
q->tail = NULL;
q->size = 0;
}
return q;
}
```
* `q_free()`
利用 q->head 為起點,並透過 next 沿著 queue 去 free elements ,在 free 掉該 element 之前,要記得先 free 掉該 element 之 value (value is a string)。
```clike
void q_free(queue_t *q)
{
/* How about freeing the list elements and the strings? */
/* Free queue structure */
if(!q)
return;
list_ele_t *current;
current=q->head;
while (current){
list_ele_t *temp;
temp=current;
current=current->next;
free(temp->value);
free(temp);
}
free(q);
return;
}
```
* `q_insert_head()`
在前端插入一個元素。題目要求在 copy 之前必須先 allocate space to string ,更重要的是要檢查 malloc 是否有成功,否則在 trace-11 中出現 malloc 失敗的情形時會無法處理。
```clike
bool q_insert_head(queue_t *q, char *s)
{
/* What should you do if the q is NULL? */
if (!q)
return false;
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
if (!newh)
return false;
/* Don't forget to allocate space for the string and copy it */
newh->value = malloc(strlen(s) + 1);
/* What if either call to malloc returns NULL? */
if (!newh->value) {
free(newh);
return false;
}
memcpy(newh->value, s, strlen(s) + 1);
newh->next = q->head;
q->head = newh;
q->size++;
if (q->size == 1)
q->tail = q->head;
return true;
}
```
* `q_insert_tail()`
插入一個元素到尾端。透過新增的 field `tail`,要插入一個元素到 queue 的尾端就不用從 head 開始走過整個 queue ,而是可以像 insert head 一樣直接將元素插入 queue 的尾端。
```clike
bool q_insert_tail(queue_t *q, char *s)
{
/* You need to write the complete code for this function */
/* Remember: It should operate in O(1) time */
if (!q)
return false;
list_ele_t *newt;
newt = malloc(sizeof(list_ele_t));
if (!newt)
return false;
newt->value = malloc(strlen(s) + 1);
if (!newt->value) {
free(newt);
return false;
}
memcpy(newt->value, s, (strlen(s) + 1));
newt->next = NULL;
if (q->size == 0) {
q->head = newt;
q->tail = newt;
} else {
q->tail->next = newt;
q->tail = newt;
}
q->size++;
return true;
}
```
* `q_remove_head()`
移除 queue 的前端元素,且在移除前須先將 string 的內容複製到 sp 中。此處採用 `strncpy` 是為了防止 buffer overflow 的問題產生。
```clike
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
/* You need to fix up this code. */
if (!q || q->size == 0)
return false;
if (sp) {
list_ele_t *temp;
size_t length = strlen(q->head->value);
strncpy(sp, q->head->value, bufsize - 1);
if (length >= bufsize)
sp[bufsize - 1] = '\0';
else
sp[length] = '\0';
temp = q->head;
q->head = q->head->next;
free(temp->value);
free(temp);
q->size--;
}
return true;
}
```
* `q_size()`
回傳 queue 中的元素個數。透過新增的 field `size` 即可知道元素個數,而不用走過整個 queue 後才能算出。
```clike
int q_size(queue_t *q)
{
/* You need to write the code for this function */
/* Remember: It should operate in O(1) time */
if (!q)
return 0;
return q->size;
}
```
* `q_reverse()`
利用三個指標來實作 reverse function ,每次進入迴圈就會將 cur->next 指向前個 node , 並讓三個指標往下個節點前進。
```clike
void q_reverse(queue_t *q)
{
/* You need to write the code for this function */
if (!q || q->size <= 1)
return;
list_ele_t *pre, *cur, *suc;
pre = NULL;
cur = q->head;
suc = cur->next;
while (suc) {
cur->next = pre;
pre = cur;
cur = suc;
suc = suc->next;
}
cur->next = pre;
q->tail = q->head;
q->head = cur;
}
```
## 解釋自動評分系統運作的原理
## 提及 qtest 的行為和裡頭的技巧
## 參考資料
- [Linked List: 新增資料、刪除資料、反轉](http://alrightchiu.github.io/SecondRound/linked-list-xin-zeng-zi-liao-shan-chu-zi-liao-fan-zhuan.html)