上次分析了雜湊表的鏈地址法的實現,即採用“數組+鏈表”的資料結構。這次,介紹下用雜湊桶的方式去解決雜湊衝突,即通過雜湊函數hash()將key轉化為雜湊值,然後根據桶的容量(桶的容量固定),且結合“除餘法”定位到某個雜湊下標,為該雜湊值建立雜湊桶(Bucket)。
完整的實現代碼:連結。下面,針對裡面關鍵的函數進行分析。 1、雜湊表的關鍵函數分析 裡面涉及的資料結構:
struct Pair {char *key;char *value;};struct Bucket {unsigned int count;Pair *pairs;};struct StrMap {unsigned int count;Bucket *buckets;};上面資料結構之間的關係可以通過下圖理解:
空雜湊表的建立:
StrMap * sm_new(unsigned int capacity){StrMap *map;map = (StrMap*)malloc(sizeof(StrMap));if (map == NULL) {return NULL;}map->count = capacity;map->buckets = (Bucket*)malloc(map->count * sizeof(Bucket));if (map->buckets == NULL) {free(map);return NULL;}memset(map->buckets, 0, map->count * sizeof(Bucket));return map;}
往雜湊表裡添加key值和value值:
int sm_put(StrMap *map, const char *key, const char *value){unsigned int key_len, value_len, index;Bucket *bucket;Pair *tmp_pairs, *pair;char *tmp_value;char *new_key, *new_value;if (map == NULL) {return 0;}if (key == NULL || value == NULL) {return 0;}key_len = strlen(key);value_len = strlen(value);/* Get a pointer to the bucket the key string hashes to */index = hash(key) % map->count;bucket = &(map->buckets[index]);/* Check if we can handle insertion by simply replacing * an existing value in a key-value pair in the bucket. */if ((pair = get_pair(bucket, key)) != NULL) {/* The bucket contains a pair that matches the provided key, * change the value for that pair to the new value. */if (strlen(pair->value) < value_len) {/* If the new value is larger than the old value, re-allocate * space for the new larger value. */tmp_value = (char*)realloc(pair->value, (value_len + 1) * sizeof(char));if (tmp_value == NULL) {return 0;}pair->value = tmp_value;}/* Copy the new value into the pair that matches the key */strcpy(pair->value, value);return 1;}/* Allocate space for a new key and value */new_key = (char*)malloc((key_len + 1) * sizeof(char));if (new_key == NULL) {return 0;}new_value = (char*)malloc((value_len + 1) * sizeof(char));if (new_value == NULL) {free(new_key);return 0;}/* Create a key-value pair */if (bucket->count == 0) {/* The bucket is empty, lazily allocate space for a single * key-value pair. */bucket->pairs = (Pair*)malloc(sizeof(Pair));if (bucket->pairs == NULL) {free(new_key);free(new_value);return 0;}bucket->count = 1;}else {/* The bucket wasn't empty but no pair existed that matches the provided * key, so create a new key-value pair. */tmp_pairs = (Pair*)realloc(bucket->pairs, (bucket->count + 1) * sizeof(Pair));if (tmp_pairs == NULL) {free(new_key);free(new_value);return 0;}bucket->pairs = tmp_pairs;bucket->count++;}/* Get the last pair in the chain for the bucket */pair = &(bucket->pairs[bucket->count - 1]);pair->key = new_key;pair->value = new_value;/* Copy the key and its value into the key-value pair */strcpy(pair->key, key);strcpy(pair->value, value);return 1;}
上述函數思路: 1)判斷雜湊表是否為空白,否則return NULL; 2) 通過函數get_pair()判斷key值是否存在雜湊表中,若存在則將相應的value值覆蓋;若不存在跳入3); 3)判斷雜湊桶是否為空白,若滿足則開闢首元素;否則即在雜湊桶的末尾開闢記憶體,添加key值和value值;
根據key值,取出相應的value
int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf){unsigned int index;Bucket *bucket;Pair *pair;if (map == NULL) {return 0;}if (key == NULL) {return 0;}index = hash(key) % map->count;bucket = &(map->buckets[index]);pair = get_pair(bucket, key);if (pair == NULL) {return 0;}if (out_buf == NULL && n_out_buf == 0) {return strlen(pair->value) + 1;}if (out_buf == NULL) {return 0;}if (strlen(pair->value) >= n_out_buf) {return 0;}strcpy(out_buf, pair->value);return 1;}
雜湊函數
static unsigned long hash(const char *str){unsigned long hash = 5381;int c;while (c = *str++) {hash = ((hash << 5) + hash) + c;}return hash;}
2、雜湊實驗
主函數:
void main(){int hash_capacity;char *str=(char*)malloc(sizeof(char)*size);printf("hash_capacity is:");scanf("hash_capacity is %d\n",&hash_capacity);StrMap *c_map=sm_new(100);sm_put(c_map,"LIN","588239");sm_put(c_map,"address","Ningbo");if (sm_get(c_map,"LIN",str,size)){printf("corresponding value is %s\n",str);}return;}
實驗結果: