# 2022q1 Homework1 (quiz1)
contributed by < `a12345645` >
## 測驗1
題目為 Leetcode [Two Sum](https://leetcode.com/problems/two-sum/)
```c
int *twoSum(int *nums, int numsSize, int target, int *returnSize)
{
map_t *map = map_init(10);
*returnSize = 0;
int *ret = malloc(sizeof(int) * 2);
if (!ret)
goto bail;
for (int i = 0; i < numsSize; i++) {
int *p = map_get(map, target - nums[i]);
if (p) { /* found */
ret[0] = i, ret[1] = *p;
*returnSize = 2;
break;
}
p = malloc(sizeof(int));
*p = i;
map_add(map, nums[i], p);
}
bail:
map_deinit(map);
return ret;
}
```
在這裡使用 hash table 實作
把 `target - nums[i]` 作為 key 使用 `map_get` 來尋找
如果在 hash table 中找到就可以與之相加為 `target` 並且回傳
沒有就把 `nums[i]` 作為 key 使用 `map_add` 加入 hash table
### hash table
#### hlist_node 與 hlist_head
```c
struct hlist_node { struct hlist_node *next, **pprev; };
struct hlist_head { struct hlist_node *first; };
```
![image alt](https://i.imgur.com/QYQLqvC.png)
在 hash table 中每個單位為 `hlist_head` 而要加入新的元素或是發生碰撞時,就會在後面接上 `hlist_node`
- 為什麼 `hlist_head` 與 `hlist_node` 的結構不一樣
因為 hash table 為了避免常常發生碰撞,通常會建的比較大有很多的空缺,而在 hash table 只需要單向的 linked list 所以使用了兩種不同結構來儲存,這樣就可以減少一半的儲存空間。
- 為什麼 `hlist_node` 的 `pprev` 會是 pointer to pointer
因為 head 跟 node 的結構是不一樣的,如果只使用 `prev` 的話就必須判斷是否回 head ,並且需要做強制的型態轉換,所以使用 `pprev` 指向前一個元素的 `struct hlist_node *next` 元素,如果是 head 就會是 `first` ,把特例變成通例。
#### map_t
```c
typedef struct { int bits; struct hlist_head *ht; } map_t;
```
`bits` 表示 `ht` 的大小為 2 的 `bit` 次方
#### map_get
```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);
}
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;
}
void *map_get(map_t *map, int key)
{
struct hash_key *kn = find_key(map, key);
return kn ? kn->data : NULL;
}
```
主要是使用 `find_key` 來在 hash table 尋找
`target = ht[hash(key)]`
把 key 通過 hash function 再去 hash table 中尋找是否有值
#### map_add
```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;`
`BBB` = `n->pprev = &h->first;`
要把新的 `hash_key` 插入 list 的頭
所以在 `AAA` 要先把原本的第一個放到 `n->next`
然後確認原本的 `first` 是否為空,把 `first->pprev` 指向 `n`
再把 head 的 `first` 指向 `n`
最後 `BBB` 是在把 `n->pprev` 指回去 head
<!--
## 測驗2
題目為 Leetcode [82. Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/)
```c
#include <stddef.h>
struct ListNode {
int val;
struct ListNode *next;
};
struct ListNode *deleteDuplicates(struct ListNode *head)
{
if (!head)
return NULL;
if (COND1) {
/* Remove all duplicate numbers */
while (COND2)
head = head->next;
return deleteDuplicates(head->next);
}
head->next = deleteDuplicates(head->next);
return head;
}
```
-->