C語言實現一個簡易的Hash table(五)

上一章中,我們使用了雙重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;
            }
        }
        // ...
    } 
    // ...
}


著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章