# 2022q1 Homework1 (quiz1)
contributed by < `vacantron` >
## 測驗一
### 運作原理
如果逐次檢查相加的數值,時間複雜度為 $O(n^2)$;若改用 `hash table` 記錄差值只與對應的索引,即可將時間複雜度降為 $O(n)$
----
### 實作
`container_of` 巨集透過利用 `offset_of` 巨集從結構體成員的指標計算出結構體的指標
```c
#define container_of(ptr, type, member) \
({ \
void *__mptr = (void *) (ptr); \
((type *) (__mptr - offsetof(type, member))); \
})
```
從以下定義得知 pprev 為一個指向前一個節點的 next ( 或是 first ) 成員的 indirect pointer
```c
struct hlist_node {
struct hlist_node *next, **pprev;
};
```
使用 indirect point 的好處是當我們要從串列中刪除節點 0 時,只要將節點 0 的 pprev 成員指向的指標重新指派給節點 1 的 pprev 成員以及更新 pprev 中指標指向的節點 ( 即前一個元素的 next ( 或是 first ) 指向的節點 ) 即可,不用管節點 0 是不是串列中的第一個元素
```graphviz
digraph {
node [shape=record];
rankdir=LR
table [label="hash_table | | | <entry>hlist_entry[n] | "];
first [label="<e>hlist_entry[n] | <h>first"];
node0 [label="<d>node0 | {<p>pprev | <nx>next}"];
node1 [label="<d>node1 | {<p>pprev | <nx>next}"];
table:entry:e -> first:e [arrowhead=none];
first:h -> node1:w;
node1:p -> first:h [weight=2];
null [label="null" shape=oval];
node1:nx -> null;
first:h:s -> node0:w [style=dashed];
node0:p -> first:h:s [style=dashed];
node1:p:s -> node0:nx:e [style=dashed];
node0:nx -> node1:w [style=dashed];
}
```
```c
void map_add(map_t *map, int key, void *data)
{
struct hash_key *kn = find_key(map, key);
if (kn)
return;
kn = malloc(sizeof(struct hash_key));
kn->key = key, kn->data = data;
struct hlist_head *h = &map->ht[hash(key, map->bits)];
struct hlist_node *n = &kn->node, *first = h->first;
AAA;
if (first)
first->pprev = &n->next;
h->first = n;
BBB;
}
```
故 `AAA` 應填入 `n->next = first` 讓新增的節點的 next 成員指向原串列的第一個元素
而 `BBB` 應填入 `n->pprev = &h->first` 連接新增的節點與串列的 head
:::spoiler outdated
在第 17 行中發現可以藉由更新 indirect pointer 指向的指標的內容來更新上一個節點的 next 指標指向的節點
```graphviz
digraph {
rankdir=LR
node [shape=record];
head [label="hlist_entry[n] | <first> *first"]
null [label="null" shape=oval]
node0 [label="<list_node> list_node_0 | { <pprev> **pprev | <next> *next }"];
node1 [label="<list_node> list_node_1 | { <pprev> **pprev | <next> *next }"];
node2 [label="<list_node> list_node_2 | { <pprev> **pprev | <next> *next }"];
head:first -> node0:list_node;
node0:next -> node1:list_node;
node1:next -> node2:list_node;
node2:next -> null;
node0:pprev -> head:first;
node1:pprev -> node0:next;
node2:pprev -> node1:next;
head->node0[style=invis]
node0->node1[style=invis]
node1->node2[style=invis]
}
```
:::
<br>
```c=
void map_deinit(map_t *map)
{
if (!map)
return;
for (int i = 0; i < MAP_HASH_SIZE(map->bits); i++) {
struct hlist_head *head = &map->ht[i];
for (struct hlist_node *p = head->first; p;) {
struct hash_key *kn = container_of(p, struct hash_key, node);
struct hlist_node *n = p;
p = p->next;
if (!n->pprev) /* unhashed */
goto bail;
struct hlist_node *next = n->next, **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
n->next = NULL, n->pprev = NULL;
bail:
free(kn->data);
free(kn);
}
}
free(map);
}
```