# 2022q1 Homework1 (quiz1)
contributed by < `TimidCactus` >
## 題目
### 測驗 1
AAA = n->next = first
BBB = n->pprev = &h->first
延伸題目1
```c
map_t *map_init(int bits) {
map_t *map = malloc(sizeof(map_t));
if (!map)
return NULL;
map->bits = bits;
map->ht = malloc(sizeof(struct hlist_head) * MAP_HASH_SIZE(map->bits));
if (map->ht) {
for (int i = 0; i < MAP_HASH_SIZE(map->bits); i++)
(map->ht)[i].first = NULL;
} else {
free(map);
map = NULL;
}
return map;
}
```
malloc 一個 pointer,裡面放大小和 hash table 的 pointer , init 成 NULL。
在 DOC 中有看到比宣告 hlist_head array 的宣告,我覺得其實可以在主程式中用那個就好了, hash table 的位置不連續就沒用了,所以一開始就知道了大小,就直接宣告。
```c
#define GOLDEN_RATIO_32 0x61C88647
static inline unsigned int hash(unsigned int val, unsigned int bits) {
/* High bits are more random, so use them. */
return (val * GOLDEN_RATIO_32) >> (32 - bits);
}
```
以前學密碼學的時候有學過這類的東西, hash 用一個大質數可以被免運算後的碰撞,這樣中胴一個桶子裡的機會就會變少,但這個不是質數卻比質數的效果來的好。
這個[論文](http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf)在備讀列中
```c
static struct hash_key *find_key(map_t *map, int key) {
struct hlist_head *head = &(map->ht)[hash(key, map->bits)];
for (struct hlist_node *p = head->first; p; p = p->next) {
struct hash_key *kn = container_of(p, struct hash_key, node);
if (kn->key == key)
return kn;
}
return NULL;
}
```
快速找到是那個桶子裡的再一個個比對 key ,並回傳整個 entry 。
```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;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
```
找出該 key 所對應的桶子,再放去第一個,O(1)的時間複雜度。
```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);
}
```
把每一個桶子裡的 node 分割出來,把原來的串也補起來。
但為什麼直接一個 while(ptr) 和 next 先存起來,把 ptr free 掉的操作,怕會存取到未定義記憶體嗎?我未見到有相關操作。