contributed by < a12345645
>
題目為 Leetcode Two Sum
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
struct hlist_node { struct hlist_node *next, **pprev; };
struct hlist_head { struct hlist_node *first; };
在 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
,把特例變成通例。
typedef struct { int bits; struct hlist_head *ht; } map_t;
bits
表示 ht
的大小為 2 的 bit
次方
#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 中尋找是否有值
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