上一章中,我们使用了双重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)的更多相关文章

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

    上一章我们解释了Hash table中最重要的hash函数,并用伪代码和C语言实现了一个我们自己的hash函数,hash函数中碰撞是无法避免的,当发生碰撞时我们改如何有效的处理呢?这章我们就来讲解下. ...

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

    说明 Hash table翻译过来就是Hash表,是一种提供了类似于关联数组的数据结构,可以通过key执行搜索.插入和删除操作.Hash表由一些列桶(buckets)组成,而每一个bucket都是由k ...

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

    上一章,我们讲了hash表的数据结构,并简单实现了hash表的初始化与删除操作,这一章我们会讲解Hash函数和实现算法,并手动实现一个Hash函数. Hash函数 本教程中我们实现的Hash函数将会实 ...

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

    上一章,简单介绍了Hash Table,并提出了本教程中要实现的几个Hash Table的方法,有search(a, k).insert(a, k, v)和delete(a, k),本章将介绍Hash ...

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

    上一章我们讲了如何根据需要动态设置hash表的大小,在第四章中,我们使用了双重哈希来解决hash表的碰撞,其实解决方法有很多,这一章我们来介绍下其他方法. 本章将介绍两种解决hash表碰撞的方法: 拉 ...

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

    上一章中,我们实现了Hash表中的插入.搜索和删除接口,我们在初始化hash表时固定了大小为53,为了方便扩展,本章将介绍如何修改hash表的大小. 设置Hash表大小 现在,我们的hash表是固定大 ...

  7. 用Java语言编写一个简易画板

    讲了三篇概博客的概念,今天,我们来一点实际的东西.我们来探讨一下如何用Java语言,编写一块简易的画图板. 一.需求分析 无论我们使用什么语言,去编写一个什么样的项目,我们的第一步,总是去分析这个项目 ...

  8. 用java语言写一个简易版本的登录页面,包含用户注册、用户登录、用户注销、修改密码等功能

    package com.Summer_0421.cn; import java.util.Arrays; import java.util.Scanner; /** * @author Summer ...

  9. PHP内核探索之变量(3)- hash table

    在PHP中,除了zval, 另一个比较重要的数据结构非hash table莫属,例如我们最常见的数组,在底层便是hash table.除了数组,在线程安全(TSRM).GC.资源管理.Global变量 ...

随机推荐

  1. SwipeRefreshLayout 报错 dispatchTouchEvent

    今天开发android中使用了 android-suport-v4 19.1 记录 SwipeRefreshLayout 的坑: http://stackoverflow.com/questions/ ...

  2. 【运维】linux命令查看端口占用情况,杀死进程,后台启动进程

    1.查看端口占用情况:> lsof -i:port COMMAND    PID    USER    FD    TYPE   DEVICE    SIZE/OFF    NODE  NAME ...

  3. Python学习---Django重点之静态资源配置

    [官网静态文件介绍] https://docs.djangoproject.com/en/1.10/howto/static-files/ # settings.py 配置静态资源文件 # STATI ...

  4. 沉淀再出发:java的文件读写

    沉淀再出发:java的文件读写 一.前言 对于java的文件读写是我们必须使用的一项基本技能,因此了解其中的原理,字节流和字符流的本质有着重要的意义. 二.java中的I/O操作 2.1.文件读写的本 ...

  5. 深入浅出SharePoint——Caml快速开发

    适用于Visual Studio 2010的Caml智能感知工具 http://visualstudiogallery.msdn.microsoft.com/15055544-fda0-42db-a6 ...

  6. OC继承

    1.成员访问类型 private:私有成员,不能被外部函数访问(使用),也不能被子类继承: protected:保护成员,不能被外部函数访问,可以被子类继承: public:公有成员,可以被外部函数访 ...

  7. [BZOJ 3441]乌鸦喝水

    3441: 乌鸦喝水 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 374  Solved: 148[Submit][Status][Discuss] ...

  8. SQL Server系统常用存储过程

    SQL Server系统存储过程也是好几百个,算了,还是写几个常用的. 1.sp_help 查询表的信息 执行存储过程: sp_help Person 显示结果如下: 妈了个B,有了这张图,你还不懂怎 ...

  9. Mongoose 利用实现HTTP服务

    嘛.... 注意:这里是使用mongoose实现HTTP服务,非数据库使用. 最近由于需要使用HTTP服务端,原先是使用的Qt框架实现的HTTP服务端,然后发现有些缺陷导致我不得不放弃这个框架,也不是 ...

  10. gluoncv 导入方式

    了解了sys.path和python 的import 的话,之前修改gluoncv 的方式就可以有了新的简单的方法: 如果你pip install gluoncv的,然后脚本又git clone了,发 ...