contributed by < zoanana990
>
1
題目
本題分兩個部分進行討論:
linux kernel
實作雜湊表的資料結構、雜湊函數two sum
的程式碼瞭解基本的雜湊表資料結構後,現在進行雜湊函數的頗析
在了解什麼是 0x61c88647
之前,我們先要了解什麼是雜湊函數(hash function),雜湊函數目的是將 <key, value>
進行映射 (mapping) ,期望輸入 key
可以利用雜湊函數得到期望的 value
,這個結果可以得到
本題使用的雜湊函數為斐波那契法,其中 0x61c88647
為 INT32
除以黃金比例所得到的結果,其可以讓一個亂數序列取得映射函數,並可以最大化分散輸入值,如下圖
如果是使用餘數法,則有可能造成一個鍵 (key) 對應多個值 (value) ,這樣就不是一個好的雜湊函數,如下所示:
測試原始碼,這邊用 python
比較方便,因此用 python
撰寫:
如果使用其他的數字呢?不使用黃金比例對於雜湊函數有什麼影響?
這邊推薦一個連結
上面我們知道,hlist_head
其實是一個陣列,因此我們可以在 的時間內利用雜湊函數找到對應的節點,然後對後續的節點進行走訪取得想要的資料,其圖示化結果如下:
假設今天使用雜湊函數算出來是 key_1
的話,會進行節點走訪,並且對應其鍵(key)值有沒有對應,若值正確,則可以使用 *map_get
函數進行資料回傳
插入節點
這邊的程式碼是想要插入節點,需要考慮到 pprev
是使用間接指標,以及程式碼中需要考慮到 first
存在的問題
也就是說,插入的節點是在 h->first
的位置,因此可以 AAA
可以填入 n->next=first
, BBB
則可以填入 n->pprev = &h->first;
因為插入節點的前一個位置是 hlist_head
,填完程式碼如下
接下來是將 hash table
刪除,這個過程比較繁瑣,但是大體上是與 linked list
一樣的。先把各個節點獨立,將指標項釋放,再將節點釋放,節點都是放完成後將 hlist_head
釋放,最後將一開始的 map
釋放,就完成了
#include <stddef.h>
#include <stdlib.h>
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;
#define MAP_HASH_SIZE(bits) (1 << bits)
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;
}
struct hash_key {
int key;
void *data;
struct hlist_node node;
};
#define container_of(ptr, type, member)
({
void *__mptr = (void *) (ptr);
((type *) (__mptr - offsetof(type, member)));
})
#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;
}
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;
}
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);
}
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;
}
2
題目
原始程式碼:
ANS:
lab0-c
裡撰寫的3
題目
4
題目