1. 程式人生 > >[譯]C語言實現一個簡易的Hash table(5)

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

現在 static 插入 int err mas const -h uri

技術分享圖片

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

實現接口

我們的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表大小中很有用:

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表中刪除比插入或搜索更復雜,因為存在碰撞,我們希望刪除的記錄可能是碰撞鏈的一部分。從表中刪除它會破壞該鏈,並且無法在鏈的尾部找到記錄。要解決此問題,我們只需將其標記為已刪除,而不是真的刪除該記錄。

我們將記錄替換為指向全局哨兵的指針,再將其標記為已刪除,該全局哨兵表示包含已刪除的記錄的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屬性減1

我們也需要修改下ht_insertht_search函數,當搜索時,我們需要忽略並跳過已刪除的項,在已刪除項的位置我們可以插入新的記錄:

// 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的記錄時,會刪除之前的記錄再插入新的記錄:

// 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表大小


原文地址:https://github.com/jamesroutley/write-a-hash-table/tree/master/05-methods

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