# 2022q1 Homework1 (quiz1)
contributed by < `Korin777` >
## Q1
### 延伸題目一
#### 函式功能:
1. map_init
用來建立 hash table `map_t` 的各個 `hlist_head` , `ht` 的數量為2的冪
```graphviz
digraph structs {
rankdir=LR
node[shape=record]
e1 [label=" <first> ht[0].first| <second> ht[1].first | <third> ht[2].first | <forth> ht[3].first"];
node[shape=none];
null_1[label = NULL]
e1:first->null_1
e1:second->null_1
e1:third->null_1
e1:forth->null_1
}
```
2. find_key
查詢 key 存在與否 , 透過 `hash function` 及 `key` 得到特定 `ht[i]` 並遍歷 `ht[i]` 找尋是否存在 `key` , 有的話回傳此 `hash_key` 指標 , 沒有則回傳 NULL
3. map_get
查詢 `key` 的 `data` , 透過 find_key 是否回傳 `hash_key` 指標來決定回傳對應的 `data` 指標或 `NULL`
5. map_add
增加 `ht[i]` 的節點 , 節點會增加在 `ht[i].first` , 先透過 `find_key` 查詢 key 是否存在 , 不存在再增加節點
```graphviz
digraph structs {
rankdir=LR
node[shape=record];
e1 [label="<p> ht[i].first "];
node[shape=record];
e2 [label=" <h>hlist_node | {<p> pprev | <n>next }"];
node[shape=record];
e3 [label=" <h>hlist_node | {<p> pprev | <n>next }"];
node[shape=none];
NULL [label="NULL"];
e1:p->e2:h;
e2:p->e1;
e2:n->e3:h;
e3:p->e2:n;
e3:n->NULL;
subgraph cluster_0 {
style=filled;
color="green";
label="new node";
e2;
}
}
```
6. map_deinit
釋放 hash table 所配置過的記憶體 , 每個 `ht[i]` 的各個節點依照由頭到尾的順序釋放 `hash_key->data` 及 `hash_key` , 最後再釋放整個 `map_t`
## Q2
### 延伸題目一
* `new_head` 為移除重複節點後的 head
* `prev_node` 用來修正上個非重複節點的下個節點
```c
struct ListNode *deleteDuplicates(struct ListNode *head)
{
if (!head)
return NULL;
struct ListNode *new_head = NULL, *prev_node = NULL;
while(head) {
while (head->next && head->val == head->next->val) // skip duplicate node
head = head->next;
if(prev_node) { // fix previous non-duplicate node's next
if(prev_node->next != head)
prev_node->next = head;
else
prev_node = head;
}
if(!new_head && head->next && head->val != head->next->val) { // Init new head
new_head = head;
prev_node = new_head;
}
head = head->next;
}
if(prev_node) { // fix previous non-duplicate node's next
if(prev_node->next != head)
prev_node->next = head;
}
return new_head;
}
```
### 延伸題目二
* 將 ListNode 結構改為作業的 circular doubly-linked list 結構
```c
struct list_head {
struct list_head *prev;
struct list_head *next;
};
typedef struct {
int value;
struct list_head list;
} element_t;
```
#### 迭代
* `tmp` 為整數指標 , 用來表示當前的重複數字
* `tmp` 為 NULL 或 `tmp` 和當前 `entry` 的數字不同 , 就看下個 `entry` 的數字是否與當前 `entry` 數字相同 , 來判斷是否該移除當前 節點及釋放對應的記憶體
* `tmp` 和當前 `entry` 的數字相同 , 直接移除當前節點及釋放對應的記憶體
* 若 `tmp` 最後不為 NULL 要釋放 `tmp` 記憶體
```c
void deleteDuplicates(struct list_head *head)
{
if (!head)
return;
int *tmp = NULL;
element_t *ele, *tmpele;
list_for_each_entry_safe (ele, tmpele, head, list) {
if (!tmp || tmp != ele->value) {
if (ele->list.next && ele->list.next != head) {
element_t *elen = list_entry(ele->list.next, element_t, list);
if (elen->value == ele->value) {
if (!tmp)
tmp = malloc(sizeof(int));
tmp = ele->value;
list_del(&ele->list);
free(ele->value), free(ele);
}
}
} else {
list_del(&ele->list);
free(ele->value), free(ele);
}
}
if (tmp)
free(tmp);
return;
}
```
#### 遞迴
* `realhead` 傳進 circular doubly-linked list 的 head(不作為 `entry` 存放 `value` 的節點), 用來判斷 list 是否以遍歷完
* 如果 `head` 與它之後的節點 `value` 重複則移除這段節點 , 並對下個非重複節點呼叫 `deleteDuplicates`
```c
void deleteDuplicates(struct list_head *head, struct list_head *realhead)
{
if (!realhead || list_empty(realhead) || list_is_singular(realhead))
return;
element_t *ele, *elen;
if(head != realhead && head->next != realhead) {
ele = list_entry(head, element_t, list);
elen = list_entry(head->next, element_t, list);
if (ele->value == elen->value) {
/* Remove all duplicate numbers */
while (ele->value == elen->value) {
head = head->next;
list_del(&ele->list);
free(ele->value), free(ele);
if(head->next == realhead) { // finish list traversal
list_del(&elen->list);
free(elen->value), free(elen);
return;
}
ele = list_entry(head, element_t, list);
elen = list_entry(head->next, element_t, list);
}
list_del(&ele->list);
free(ele->value), free(ele);
return deleteDuplicates(head->next, realhead);
}
}
else // finish list traversal
return;
return deleteDuplicates(head->next, realhead);
}
```
## Q3
#### 資料結構
* LRUCache 結構如下 , `capacity=5` 為 `hheads` 數量及總共能存的 `LRUNode` 數量 , `count=5` 為目前 `LRUNode` 數量 , 能透過 `dhead` 或 `hheads[i]` 來取得 `LRUNode`
```graphviz
digraph structs {
rankdir=LR
node[shape=record]
h1 [label=" <d>dhead |<h0> hheads[0]| <h1> hheads[1] | <h2> hheads[2] | <h3> hheads[3] | <h4> hheads[4]"];
node[shape=record];
e1 [label="{key | value } |<h>hlink1 | {<p> prev | <n>next } | <d>dlink1 | {<p1> prev | <n2>next }"];
node[shape=record];
e2 [label="{key | value } |<h>hlink2 | {<p> prev | <n>next } | <d>dlink2 | {<p1> prev | <n2>next }"];
node[shape=record];
e3 [label="{key | value } |<h>hlink1 | {<p> prev | <n>next } | <d>dlink3 | {<p1> prev | <n2>next }"];
node[shape=record];
e4 [label="{key | value } |<h>hlink1 | {<p> prev | <n>next } | <d>dlink4 | {<p1> prev | <n2>next }"];
node[shape=record];
e5 [label="{key | value } |<h>hlink2 | {<p> prev | <n>next } | <d>dlink5 | {<p1> prev | <n2>next }"];
h1:h0->e1:h
e1:n->e2:h
h1:h1->e3:h
h1:h2->e4:h
e4:n->e5:h
h1:d->e1:d
e1:n2->e2:d
e2:n2->e3:d
e3:n2->e4:d
e4:n2->e5:d
}
```
* 透過 `hheads[i]` , 只能拿到屬於這個區塊的 `LRUNode`
```graphviz
digraph structs {
rankdir=LR
node[shape=record];
e1 [label="<h> hheads[i] | {<p> prev | <n>next }"];
node[shape=record];
e2 [label="<h>hlink1 | {<p> prev | <n>next }"];
node[shape=record];
e3 [label="<h>hlink2 | {<p> prev | <n>next }"];
node[shape=record];
e1:n->e2:h;
e2:p->e1:h;
e2:n->e3:h;
e3:p->e2:h;
e3:n->hheads;
e1:p->hlink2
subgraph cluster_0 {
style=filled;
color="green";
label="LRUNode";
e2;
}
subgraph cluster_1 {
style=filled;
color="yellow";
label="LRUCache";
e1;
}
subgraph cluster_3 {
style=filled;
color="green";
label="LRUNode";
e3;
}
}
```
* 透過 `dhead` 可以遍歷所有的 `LRUNode` , `LRUNode` 越後面表示越久沒用到
```graphviz
digraph structs {
rankdir=LR
node[shape=record];
e1 [label="<d> dhead | {<p> prev | <n>next }"];
node[shape=record];
e2 [label="<h>dlink1 | {<p> prev | <n>next }"];
node[shape=record];
e3 [label="<h>dlink2 | {<p> prev | <n>next }"];
node[shape=record];
e1:p->dlink2
e1:n->e2:h;
e2:p->e1:d;
e2:n->e3:h;
e3:p->e2:h;
e3:n->dhead
subgraph cluster_0 {
style=filled;
color="green";
label="LRUNode";
e2;
}
subgraph cluster_1 {
style=filled;
color="yellow";
label="LRUCache";
e1;
}
subgraph cluster_3 {
style=filled;
color="green";
label="LRUNode";
e3;
}
}
```
#### 函式功能
1. lRUCacheCreate
用來建立 `LRUCache` 及其中的 `hheads[]`
2. lRUCacheFree
用來釋放 `LRUCache` 及其所擁有的 `LRUNode`
3. lRUCacheGet
透過 `key` 來取得 `LRUCache` 對應的 `LRUNode->value` , 如果不存在則回傳 -1
4. lRUCachePut
* 新增或修改 `LRUNode`
* 如果 `key` 存在會修改 `LRUNode->value` , 並將此 `LRUNode->dlink` 移到 `LRUCache->dhead` 最前面 , 表示最近用到
* 如果 `key` 不存在則會看 `LRUCache->count` 是否已達到 `LRUCache->capacity` , 是的話就移除 `LRUCache->dlink` 最後一個 `LRUNode(最久沒用到)` , 並新增新的 `key-value` 到該 `LRUNode` 並加到 `LRUCache->dlink` 及對應的 `LRUCache->hheads[]` , 反之則會配置一個新的 `LRUNode` 空間
## Q4
### 延伸問題一
#### 資料結構
題目結構如下 , `nums = [100,4,200,1,3,2]` 、 `n_size=6` , `heads` 數量即為 `n_size` 數 , 每個 `num` 對 `n_size` 取餘數放到對應的 `heads[num < 0 ? -num % size : num % size]`
```graphviz
digraph structs {
rankdir=LR
node[shape=record]
h1 [label="<h0> heads[0]| <h1> heads[1] | <h2> heads[2] | <h3> heads[3] | <h4> heads[4] | <h5> heads[5]"];
e1 [label="<num>num=100 |<l>link | {<p> prev | <n>next }"];
e2 [label="<num>num=4 |<l>link | {<p> prev | <n>next }"];
e3 [label="<num>num=200 |<l>link | {<p> prev | <n>next }"];
e4 [label="<num>num=1 |<l>link | {<p> prev | <n>next }"];
e5 [label="<num>num=3 |<l>link | {<p> prev | <n>next }"];
e6 [label="<num>num=2 |<l>link | {<p> prev | <n>next }"];
h1:h0->e3:l
e3:n->e1:l
h1:h1->e4:l
h1:h2->e6:l
h1:h3->e5:l
h1:h4->e2:l
}
```
`heads[i]` 後面連結屬與此區塊的 `seq_node`
```graphviz
digraph structs {
rankdir=LR
node[shape=record];
e1 [label="<h> heads | {<p> prev | <n>next }"];
node[shape=record];
e2 [label="num=200 | <h>link1 | {<p> prev | <n>next }"];
node[shape=record];
e3 [label="num=100 | <h>link2 | {<p> prev | <n>next }"];
node[shape=record];
e1:n->e2:h;
e2:p->e1:h;
e2:n->e3:h;
e3:p->e2:h;
e3:n->heads;
e1:p->link2
subgraph cluster_0 {
style=filled;
color="green";
label="seq_node";
e2;
}
subgraph cluster_1 {
style=filled;
color="yellow";
label="list_head";
e1;
}
subgraph cluster_3 {
style=filled;
color="green";
label="seq_node";
e3;
}
}
```
#### 函式功能
1. find
用來尋找 `num` 是否存在對應 `heads[i]` 中 , 有就回傳此 `seq_node` , 沒有則回傳 NULL
2. longestConsecutive
* 用來找出最長連續整數長度
* 首先配置長度 `n_size` 的 `heads` 並將 `nums` 的數字依序以 `seq_node` 的型態插入對應的 `heads[i]`
* 遍歷 `nums` 中的數字 `num` , 如果 `num` 存在於對應 `heads[i]` 中 , 則繼續尋找與 `num` 相連的所有數字
* `left` 及 `right` 初始為 `num` ,`left` 表示比 `num` 小的相鄰數字 , `right` 則表示比 `num` 大的相鄰數字
* 持續將 `left` 減1並查詢其是否存在與對應 `heads[i]` , 存在的話將 `heads[i]` 中的 `left` 移除 , 反之則表示已經沒有比 `num` 小的相鄰數字 , 再來持續將 `right` 加1並查詢 `right` 是否存在與對應 `heads[i]` , 存在的話將 `heads[i]` 中的 `right` 移除 , 反之則表示已經沒有比 `num` 大的相鄰數字
* 更新最長相鄰數字的長度
#### 改進與實作
在 `longestConsecutive` 新增釋放記憶體的 code 避免 memory leak
```c
int longestConsecutive(int *nums, int n_size)
{
int hash, length = 0;
struct seq_node *node;
struct list_head *heads = malloc(n_size * sizeof(*heads));
for (int i = 0; i < n_size; i++)
INIT_LIST_HEAD(&heads[i]);
for (int i = 0; i < n_size; i++) {
if (!find(nums[i], n_size, heads)) {
hash = nums[i] < 0 ? -nums[i] % n_size : nums[i] % n_size;
node = malloc(sizeof(*node));
node->num = nums[i];
list_add(&node->link, &heads[hash]);
}
}
for (int i = 0; i < n_size; i++) {
int len = 0;
int num;
node = find(nums[i], n_size, heads);
while (node) {
len++;
num = node->num;
list_del(&node->link);
free(node);
int left = num, right = num;
while ((node = find(--left, n_size, heads))) {
len++;
list_del(&node->link);
free(node);
}
while ((node = find(++right, n_size, heads))) {
len++;
list_del(&node->link);
free(node);
}
length = len > length ? len : length;
}
}
struct seq_node *seq, *tmp;
for (int i = 0; i < n_size; i++)
list_for_each_entry_safe(seq,tmp,&heads[i],link)
list_del(&seq->link), free(seq);
free(heads);
return length;
}
```
### 延伸問題二
參考 Q1 的 hash table 實作 , 結構改為如下
```c
struct hlist_node { struct hlist_node *next, **pprev; };
struct hlist_head { struct hlist_node *first; };
typedef struct { int bits; struct hlist_head *ht; } map_t;
struct hash_key {
int key;
void *data;
struct hlist_node node;
};
```
沿用 Q1 程式碼 , 並參考 [linux/list.h](https://github.com/torvalds/linux/blob/master/include/linux/list.h) `__hlist_del` 函式實作 `map_del` 用來移除 hash table 節點 `hlist_node`
```c
void map_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if(next)
next->pprev = pprev;
}
```
修改 `longestConsecutive` , 以 `map_t` 來尋找最長連續整數長度
```c
int longestConsecutive(int *nums, int n_size)
{
map_t *map = map_init(10);
int length = 0;
for (int i = 0; i < n_size; i++) {
int *p = map_get(map,nums[i]);
if(!p) {
p = malloc(sizeof(int));
*p = nums[i];
map_add(map,nums[i],p);
}
}
for (int i = 0; i < n_size; i++) {
int len = 0;
int *num;
struct hash_key *hk = find_key(map, i);
while (hk) {
len++;
num = hk->data;
int left = *num, right = *num;
map_del(&hk->node);
free(hk->data);
free(hk);
while ((hk = find_key(map,--left))) {
len++;
map_del(&hk->node);
free(hk->data);
free(hk);
}
while ((hk = find_key(map,++right))) {
len++;
map_del(&hk->node);
free(hk->data);
free(hk);
}
length = len > length ? len : length;
}
}
map_deinit(map);
return length;
}
```
[題目連結](https://hackmd.io/@sysprog/linux2022-quiz1)