[譯]C語言實現一個簡易的Hash table(5)

上一章中,咱們使用了雙重Hash的技術來處理碰撞,並用了C語言實現,本章咱們將實現Hash表中的插入搜索刪除接口。segmentfault

實現接口

咱們的hash函數將會實現以下的接口:函數

// hash_table.h
void ht_insert(ht_hash_table* ht, const char* key, const char* value);
char* ht_search(ht_hash_table* ht, const char* key);
void ht_delete(ht_hash_table* ht, const char* key);

Insert函數

hash表中插入一條記錄時,咱們須要遍歷整個hash表知道找到一個空的位置,而後執行插入並將hash表的大小加1hash表中的count屬性表明hash表的大小,在下一章縮放hash表大小中頗有用:spa

void ht_insert(ht_hash_table* ht, const char* key, const char* value) {
    ht_item* item = ht_new_item(key, value);
    int index = ht_get_hash(item->key, ht->size, 0);
    ht_item* cur_item = ht->items[index];
    int i = 1;
    while(cur_item != NULL) {
        index = ht_get_hash(item->key, ht->size, i);
        cur_item = ht->items[index];
        ++i;
    }
    ht->items[index] = item;
    ht->count++;
}

Search函數

searchinsert有點類似,可是在while循環中,咱們會檢查記錄的key是否與咱們正在搜索的key匹配。若是匹配,就會返回這條記錄的value,沒有匹配到就會返回NULL指針

char* ht_search(ht_hash_table* ht, const char* key) {
        int index = ht_get_hash(key, ht->size, 0);
        ht_item* item = ht->items[index];
        int i = 1;
        while (item != NULL) {
            if (strcmp(item->key, key) == 0) {
                return item->value;
            }
            index = ht_get_hash(key, ht->size, i);
            item = ht->items[index];
            i++;
        } 
    return NULL;
}

delete函數

從開放的地址hash表中刪除比插入或搜索更復雜,由於存在碰撞,咱們但願刪除的記錄多是碰撞鏈的一部分。從表中刪除它會破壞該鏈,而且沒法在鏈的尾部找到記錄。要解決此問題,咱們只需將其標記爲已刪除,而不是真的刪除該記錄。code

咱們將記錄替換爲指向全局哨兵的指針,再將其標記爲已刪除,該全局哨兵表示包含已刪除的記錄的bucket接口

// hash_table.c
static ht_item HT_DELETED_ITEM = {NULL, NULL};

void ht_delete(ht_hash_table* ht, const char* key) {
    int index = ht_get_hash(key, ht->size, 0);
    ht_item* item = ht->items[index];
    int i = 1;
    while (item != NULL) {
        if (item != &HT_DELETED_ITEM) {
            if (strcmp(item->key, key) == 0) {
                ht_del_item(item);
                ht->items[index] = &HT_DELETED_ITEM;
            }
        }
        index = ht_get_hash(key, ht->size, i);
        item = ht->items[index];
        i++;
    } 
    ht->count--;
}

刪除後,咱們須要將hash表count屬性減1get

咱們也須要修改下ht_insertht_search函數,當搜索時,咱們須要忽略並跳過已刪除的項,在已刪除項的位置咱們能夠插入新的記錄:hash

// hash_table.c
void ht_insert(ht_hash_table* ht, const char* key, const char* value) {
    // ...
    while (cur_item != NULL && cur_item != &HT_DELETED_ITEM) {
        // ...
    }
    // ...
}

char* ht_search(ht_hash_table* ht, const char* key) {
    // ...
    while (item != NULL) {
        if (item != &HT_DELETED_ITEM) { 
            if (strcmp(item->key, key) == 0) {
                return item->value;
            }
        }
        // ...
    }
    // ...
}

修改一下

咱們的hash表如今還不支持更新key的值,若是咱們插入兩條相同key的記錄,key將會衝突,第二條記錄就會插入到下一個可用的位置,當使用key搜索時,咱們會找到第一條記錄,第二條記錄就永遠不會被找到,如今咱們修改下ht_insert函數,在插入多條相同key的記錄時,會刪除以前的記錄再插入新的記錄:it

// hash_table.c
void ht_insert(ht_hash_table* ht, const char* key, const char* value) {
    // ...
    while (cur_item != NULL) {
        if (cur_item != &HT_DELETED_ITEM) {
            if (strcmp(cur_item->key, key) == 0) {
                ht_del_item(cur_item);
                ht->items[index] = item;
                return;
            }
        }
        // ...
    } 
    // ...
}

上一章:處理碰撞
下一章:縮放Hash表大小table

相關文章
相關標籤/搜索