# facebooc-q0 - [github](https://github.com/shjjHhj126/facebooc-q0) - acheivement : queue.c 補code ### result ![](https://i.imgur.com/psd0UAz.png) ### struct used ```c typedef struct element { /* Pointer to array holding string. * This array needs to be explicitly allocated and freed */ char *value; struct element *next; } element_t; typedef struct { element_t *head; /* Linked list of elements */ element_t *tail; size_t size; } queue_t; ``` ### q_new() malloc a block of memory of queue_t ```c /* * Create empty queue. * Return NULL if could not allocate space. */ queue_t *q_new() { queue_t *myqueue = (queue_t*)malloc(sizeof(queue_t)); if(!myqueue) return false;//coz if not enough memory, malloc() may cause error myqueue->head = myqueue->tail = NULL; myqueue->size = 0; return myqueue; } ``` ### q_free() 1. free the `element_t`'s value 2. free the `element_t` ```c /* Free all storage used by queue */ void q_free(queue_t *q) { if (!q) return; element_t *tmp; while (q->head != q->tail) { tmp = q->head; q->head = q->head->next; free(tmp->value); free(tmp); } free(q->head->value); free(q->head); free(q); } ``` ### q_insert_head() 1. `strcpy` vs `strncpy` vs `strdup` vs `memcpy` : - `strncpy` 比 `strncpy` 安全,因為`strcpy` 會複製到遇到`\0`為止,**但是`\0`有時不會出現**,然後報錯!。`strncpy(dest,src,size)` 需使用者自己輸入src's size,**超過就不複製**。(`strncpy`優於`strcpy`只在於最後須傳一個size,就是"人工"避掉 ==) - 但前兩者用在此處都不理想(以粗體標示不理想處),原先我使用`strncpy`處理,後來看到有人用`strdup`會在`dest`無足夠空間時dynamic allocate memory。nice sol! - `memcpy` : `memcpy` 無腦按記憶體實際儲存數值(0/1)複製,但string算有點複雜的結構,為怕出錯,所以先不用。 2. 處理dynamic memory allocation 無法運作的情況 3. 需處理`q->head == NULL`的情況 4. `newElement->value == NULL`時,要先`free(newElement)` ```c /* * Attempt to insert element at head of queue. * Return true if successful. * Return false if q is NULL or could not allocate space. * Argument s points to the string to be stored. * The function must explicitly allocate space and copy the string into it. */ bool q_insert_head(queue_t *q, char *s) { if(!q) return false; element_t *newElement = (element_t *)malloc(sizeof(element_t)); if(!newElement) return false;//need this code? newElement->value = strdup(s);//strdup already malloc?? // handle failure of allocation if(!newElement->value) { free(newElement);// 這行要加!常錯! return false; } // move head:head might be NULL newElement->next = q->head; if(!q->head) q->tail = newElement; q->head = newElement; q->size += 1; return true; } ``` :::warning `element_t *newElement = (element_t *)malloc(sizeof(element_t));` 會造成 leak 請小心處理,原因在 `newElement->value = strdup(s);` 的 false case。 :notes: Astus ::: :::success 已更正在4. :pencil: 庭子 ::: ### q_insert_tail() 跟`q_insert_head()`差不多 ```c /* * Attempt to insert element at tail of queue. * Return true if successful. * Return false if q is NULL or could not allocate space. * Argument s points to the string to be stored. * The function must explicitly allocate space and copy the string into it. */ bool q_insert_tail(queue_t *q, char *s) { if(!q) return false; element_t *newElement = (element_t *)malloc(sizeof(element_t)); if(!newElement) return false; newElement->value = strdup(s); // handle failure of allocation if(!newElement->value) { free(newElement); return false; } newElement->next=NULL; // move tail:tail might be NULL if(!q->tail) q->head = newElement; else{ q->tail->next = newElement; } q->tail = newElement; q->size+=1; return true; } ``` ### q_remove_head() 1. why move to *sp? 不知道,題目要求。 2. why use bufsize-1 instead of bufsize? 因為`strncpy`遇到`\`就停止,所以不會複製到`\0`,所以之後再加上`\0`。 ```c /* * Attempt to remove element from head of queue. * Return true if successful. * Return false if queue is NULL or empty. * If sp is non-NULL and an element is removed, copy the removed string to *sp * (up to a maximum of bufsize-1 characters, plus a null terminator.) * The space used by the list element and the string should be freed. */ bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (!q || q->size == 0) return false; element_t *tmp = q->head; q->head = q->head->next; if(sp){ strncpy(sp,tmp->value,bufsize-1);//sp's size = bufsize } sp[bufsize] = '\0';//之後再加上`\0` free(tmp->value); free(tmp); q->size -= 1; return true; } ``` ### q_size() 就醬 :::info 不要裝可愛,我們沒有在做醬汁,沒有「就醬」這種醬 >< :notes: Astus ::: ```c /* * Return number of elements in queue. * Return 0 if q is NULL or empty */ size_t q_size(queue_t *q) { if(!q)return 0; return q->size; } ``` ### q_reverse() 先用之前leetcode寫過的解法 有空學jserv用pointer to pointer處理的技巧 ```c /* * Reverse elements in queue * No effect if q is NULL or empty * This function should not allocate or free any list elements * (e.g., by calling q_insert_head, q_insert_tail, or q_remove_head). * It should rearrange the existing ones. */ void q_reverse(queue_t *q) // jserv use ** to solve this:shortcoming? { if (!q) return; q->tail = q->head; // move q->tail element_t *pre, *cur, *nxt; nxt = pre = NULL; cur = q->head; while (cur) { nxt = cur->next; // move next cur->next = pre; // reverse cur->next pre = cur; // move pre cur = nxt; // move cur } q->head = pre; // move q->head } ``` ### merge_sort() 參考 [geeksforgeeks](https://www.geeksforgeeks.org/merge-sort-for-linked-list/) 1. when to use `**` and `*`? `**` saves the change, `*` is just for indicating. 2. `split()`使用快慢指針 3. `merge()`因為上述1.,所以使用`*` ```c /* * The function's sorting algorithm should be merge sort. */ void split(element_t *head, element_t **front, element_t **back) { element_t *fast, *slow; slow = head; fast = head->next; while (fast) { fast = fast->next; if (fast) { slow = slow->next; fast = fast->next; } } *front = head; *back = slow->next; slow->next = NULL; } //as mentioned in split(),here use *(not sure) element_t *merge(element_t *a, element_t *b) { element_t *result = NULL; // base case if (!a) return b; else if (!b) return a; // Pick either front or back, and recur if (strcmp(a->value, b->value) <= 0) { result = a; result->next = merge(a->next, b); } else { result = b; result->next = merge(a, b->next); } return result; } void merge_sort(element_t **head) { if (!(*head) || !(*head)->next) return; element_t *a = NULL; element_t *b = NULL; split(*head, &a, &b); // &(**head)? merge_sort(&a); merge_sort(&b); *head = merge(a, b); } ``` ### q_sort() 就醬 ```c /* * Sort elements of queue in ascending order * No effect if q is NULL or empty. In addition, if q has only one * element, do nothing. */ void q_sort(queue_t *q) { if (!q || !q->head || q->size == 1) return; merge_sort(&q->head); // so q->head point at the smallest value element_t *walk = q->head; while(walk->next){ walk=walk->next; } q->tail=walk; } ```