[译]C语言实现一个简易的Hash table(5)

上一章中,咱们使用了双重Hash的技术来处理碰撞,并用了C语言实现,本章咱们将实现Hash表中的插入搜索删除接口。函数

实现接口

咱们的hash函数将会实现以下的接口:.net

// 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,没有匹配到就会返回NULLcode

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表中删除比插入或搜索更复杂,由于存在碰撞,咱们但愿删除的记录多是碰撞链的一部分。从表中删除它会破坏该链,而且没法在链的尾部找到记录。要解决此问题,咱们只需将其标记为已删除,而不是真的删除该记录。blog

咱们将记录替换为指向全局哨兵的指针,再将其标记为已删除,该全局哨兵表示包含已删除的记录的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

相关文章
相关标签/搜索