---
tag: linux2022
---
# 2022q1 Homework1 (lab0)
contributed by < `ArielWu0203` >
## queue.c 實作
- [x] `q_new`: 建立新的「空」佇列;
- [x] `q_free`: 釋放佇列所佔用的記憶體;
- [x] `q_insert_head`: 在佇列開頭 (head) 插入 (insert) 給定的新節點 (以 LIFO 準則);
- [x] `q_insert_tail`: 在佇列尾端 (tail) 插入 (insert) 給定的新節點 (以 FIFO 準則);
- [x] `q_remove_head`: 在佇列開頭 (head) 移去 (remove) 給定的節點;
- [x] `q_release_element`: 釋放特定節點的記憶體空間;
- [x] `q_size`: 計算目前佇列的節點總量;
- [x] `q_delete_mid`: 移走佇列中間的節點,詳見 [LeetCode 2095. Delete the Middle Node of a Linked List](https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/)
- [ ] `q_delete_dup`: 在已經排序的狀況,移走佇列中具備重複內容的節點,詳見 [LeetCode 82. Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/)
- [x] `q_swap`: 交換佇列中鄰近的節點,詳見 [LeetCode 24. Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/)
- [x] `q_reverse`: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列節點,換言之,它只能重排已經存在的節點;
- [ ] `q_sort`: 以==遞增順序==來排序鏈結串列的節點,可參閱 [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/) 得知實作手法;
### q_new
```c
/*
* Create empty queue.
* Return NULL if could not allocate space.
*/
struct list_head *q_new()
{
struct list_head *head = malloc(sizeof(struct list_head));
if(!head)
return NULL;
INIT_LIST_HEAD(head);
return head;
}
```
使用 `INIT_LIST_HEAD` 對 `head` 初始化。
```c
// in <list.h>
static inline void INIT_LIST_HEAD(struct list_head *head)
{
head->next = head;
head->prev = head;
}
```
### q_insert_head
```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(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
else {
// Init ele
ele->value = strdup(s);
INIT_LIST_HEAD(&(ele->list));
// Add a list node to the beginning of the list
list_add(&(ele->list), head);
}
return true;
}
```
```c
/**
* list_add() - Add a list node to the beginning of the list
* @node: pointer to the new node
* @head: pointer to the head of the list
*/
static inline void list_add(struct list_head *node, struct list_head *head)
{
struct list_head *next = head->next;
next->prev = node;
node->next = next;
node->prev = head;
head->next = node;
}
```
### q_insert_tail
```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(struct list_head *head, char *s)
{
if (!head)
return false;
element_t *ele = malloc(sizeof(element_t));
if (!ele)
return false;
else {
// Init ele
ele->value = strdup(s);
INIT_LIST_HEAD(&(ele->list));
// Add a list node to the beginning of the list
list_add_tail(&(ele->list), head);
}
return true;
}
```
```c
/**
* list_add_tail() - Add a list node to the end of the list
* @node: pointer to the new node
* @head: pointer to the head of the list
*/
static inline void list_add_tail(struct list_head *node, struct list_head *head)
{
struct list_head *prev = head->prev;
prev->next = node;
node->next = head;
node->prev = prev;
head->prev = node;
}
```
### q_free
```c
/* Free all storage used by queue */
void q_free(struct list_head *l)
{
if(!l)
return;
element_t *ele, *ele_safe;
list_for_each_entry_safe (ele, ele_safe, l, list) {
q_release_element(ele);
}
free(l);
}
```
`list_for_each_entry_safe` 會將 `entry->next` 儲存在 `safe` ,所以當刪除 `entry` 時,也不會影響到下一個迴圈的 `entry`
* list_for_each_entry_safe - iterate over list entries and allow deletes
@entry: pointer used as iterator
@safe: @type pointer used to store info for next entry in list
@head: pointer to the head of the list
@member: name of the list_head member variable in struct type of @entry
### q_size
```c
/*
* Return number of elements in queue.
* Return 0 if q is NULL or empty
*/
int q_size(struct list_head *head)
{
if (!head)
return 0;
int len = 0;
struct list_head *ptr;
list_for_each (ptr, head)
len++;
return len;
}
```
### q_remove_head
```c
/*
* Attempt to remove element from head of queue.
* Return target element.
* Return NULL 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.)
*
* NOTE: "remove" is different from "delete"
* The space used by the list element and the string should not be freed.
* The only thing "remove" need to do is unlink it.
*
* REF:
* https://english.stackexchange.com/questions/52508/difference-between-delete-and-remove
*/
element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize)
{
if (!head || list_empty(head))
return NULL;
element_t *node = list_entry(head->next, element_t, list);
list_del(head->next);
if (sp)
strncpy(sp, node->value, strlen(node->value) + 1);
return node;
}
```
`list_entry` 可以得到 `entry` 的位址
* list_entry() - Calculate address of entry that contains list node
> @node: pointer to list node
> @type: type of the entry containing the list node
> @member: name of the list_head member variable in struct @type
> Return: @type pointer of entry containing node
### q_delete_mid
```c
/*
* Delete the middle node in list.
* The middle node of a linked list of size n is the
* ⌊n / 2⌋th node from the start using 0-based indexing.
* If there're six element, the third member should be return.
* Return true if successful.
* Return false if list is NULL or empty.
*/
bool q_delete_mid(struct list_head *head)
{
// https://leetcode.com/problems/delete-the-middle-node-of-a-linked-list/
if (!head || list_empty(head))
return false;
int step = q_size(head) / 2;
struct list_head *node = head->next;
while (step--) {
node = node->next;
}
list_del(node);
q_release_element(list_entry(node, element_t, list));
return true;
}
```
### q_swap
```c
/*
* Attempt to swap every two adjacent nodes.
*/
void q_swap(struct list_head *head)
{
// https://leetcode.com/problems/swap-nodes-in-pairs/
if (!head || list_empty(head))
return;
struct list_head *first = head->next, *second = head->next->next;
while (first != head && second != head) {
first->next = second->next;
first->next->prev = first;
second->prev = first->prev;
second->prev->next = second;
first->prev = second;
second->next = first;
first = first->next;
second = first->next;
}
}
```
> 尾端的 `return` 敘述非必要
> :notes: jserv
### q_reverse
```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(struct list_head *head) {
if(!head || list_empty(head))
return;
struct list_head *ptr = head;
do {
struct list_head *tmp = ptr->prev;
ptr->prev = ptr->next;
ptr->next = tmp;
ptr = ptr->prev;
} while (ptr!=head);
return;
}
```
### q_delete_dup
```c
/*
* Delete all nodes that have duplicate string,
* leaving only distinct strings from the original list.
* Return true if successful.
* Return false if list is NULL.
*
* Note: this function always be called after sorting, in other words,
* list is guaranteed to be sorted in ascending order.
*/
bool q_delete_dup(struct list_head *head)
{
// https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
if (!head)
return false;
struct list_head *ptr = head->next;
while (ptr != head && ptr->next != head) { // ptr is not the tail or head
element_t *first = list_entry(ptr, element_t, list);
element_t *second = list_entry(ptr->next, element_t, list);
if (!strcmp(first->value, second->value)) {
while (second && !strcmp(first->value, second->value)) {
list_del(&first->list);
q_release_element(first);
first = second;
if (first->list.next != head) {
second = list_entry(first->list.next, element_t, list);
} else {
second = NULL;
}
}
ptr = first->list.next;
list_del(&first->list);
q_release_element(first);
} else {
ptr = ptr->next;
}
}
return true;
}
```
### 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.
*/
struct list_head *mergesort_list(struct list_head *head);
struct list_head *merge_two_lists(struct list_head *L1, struct list_head *L2);
void q_sort(struct list_head *head)
{
if (!head || list_empty(head))
return;
head->prev->next = NULL;
head->next = mergesort_list(head->next);
struct list_head *ptr = head;
for (; ptr && ptr->next; ptr = ptr->next) {
ptr->next->prev = ptr;
}
ptr->next = head;
head->prev = ptr;
}
struct list_head *mergesort_list(struct list_head *head)
{
if (!head || !head->next)
return head;
struct list_head *slow = head;
for (struct list_head *fast = head->next; fast && fast->next;
fast = fast->next->next)
slow = slow->next;
struct list_head *mid = slow->next;
slow->next = NULL;
struct list_head *left = mergesort_list(head), *right = mergesort_list(mid);
return merge_two_lists(left, right);
}
struct list_head *merge_two_lists(struct list_head *L1, struct list_head *L2)
{
struct list_head *head = NULL, **ptr = &head, **node;
for (node = NULL; L1 && L2; *node = (*node)->next) {
node = (strcmp(list_entry(L1, element_t, list)->value,
list_entry(L2, element_t, list)->value) <= 0)
? &L1
: &L2;
*ptr = *node;
ptr = &(*ptr)->next;
}
if (L1)
*ptr = L1;
else
*ptr = L2;
return head;
}
```