[译]C语言实现一个简易的Hash table(5)
上一章中,我们使用了双重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表
的大小加1
。hash表
中的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函数
search
和insert
有点相似,但是在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_insert
和ht_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)的更多相关文章
- [译]C语言实现一个简易的Hash table(4)
上一章我们解释了Hash table中最重要的hash函数,并用伪代码和C语言实现了一个我们自己的hash函数,hash函数中碰撞是无法避免的,当发生碰撞时我们改如何有效的处理呢?这章我们就来讲解下. ...
- [译]C语言实现一个简易的Hash table(1)
说明 Hash table翻译过来就是Hash表,是一种提供了类似于关联数组的数据结构,可以通过key执行搜索.插入和删除操作.Hash表由一些列桶(buckets)组成,而每一个bucket都是由k ...
- [译]C语言实现一个简易的Hash table(3)
上一章,我们讲了hash表的数据结构,并简单实现了hash表的初始化与删除操作,这一章我们会讲解Hash函数和实现算法,并手动实现一个Hash函数. Hash函数 本教程中我们实现的Hash函数将会实 ...
- [译]C语言实现一个简易的Hash table(2)
上一章,简单介绍了Hash Table,并提出了本教程中要实现的几个Hash Table的方法,有search(a, k).insert(a, k, v)和delete(a, k),本章将介绍Hash ...
- [译]C语言实现一个简易的Hash table(7)
上一章我们讲了如何根据需要动态设置hash表的大小,在第四章中,我们使用了双重哈希来解决hash表的碰撞,其实解决方法有很多,这一章我们来介绍下其他方法. 本章将介绍两种解决hash表碰撞的方法: 拉 ...
- [译]C语言实现一个简易的Hash table(6)
上一章中,我们实现了Hash表中的插入.搜索和删除接口,我们在初始化hash表时固定了大小为53,为了方便扩展,本章将介绍如何修改hash表的大小. 设置Hash表大小 现在,我们的hash表是固定大 ...
- 用Java语言编写一个简易画板
讲了三篇概博客的概念,今天,我们来一点实际的东西.我们来探讨一下如何用Java语言,编写一块简易的画图板. 一.需求分析 无论我们使用什么语言,去编写一个什么样的项目,我们的第一步,总是去分析这个项目 ...
- 用java语言写一个简易版本的登录页面,包含用户注册、用户登录、用户注销、修改密码等功能
package com.Summer_0421.cn; import java.util.Arrays; import java.util.Scanner; /** * @author Summer ...
- PHP内核探索之变量(3)- hash table
在PHP中,除了zval, 另一个比较重要的数据结构非hash table莫属,例如我们最常见的数组,在底层便是hash table.除了数组,在线程安全(TSRM).GC.资源管理.Global变量 ...
随机推荐
- ubuntu16下面 redis 无法链接到客户端问题
1.今天从github上面下载了一个项目,链接到自己的tomcat里面的redis,结果在虚拟机里面可以链接成功,但是在客户端总是提示链接失败.google之后,原来是 因为 需要在redis里面设置 ...
- ORACLE 数据找回
-- 找回一个小时前的数据 select * from sys_system_dictionary as of timestamp sysdate - 1/24order by id AS OF TI ...
- [BZOJ 1972][Sdoi2010]猪国杀
1972: [Sdoi2010]猪国杀 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 364 Solved: 204[Submit][Status][D ...
- python 获取当前目录,上级目录,上上级目录
import os print '***获取当前目录***' print os.getcwd() print os.path.abspath(os.path.dirname(__file__)) pr ...
- Linux和Unix的区别
Linux Unix 免费 收费 开源 不开源 硬件无要求 有要求 IBM Sun Hp 主流的Linux的发型版本: RedHat Fedora Mand ...
- cogs [HZOI 2015]有标号的二分图计数
题目分析 n个点的二分染色图计数 很显然的一个式子 \[ \sum_{i=0}^n\binom{n}{i}2^{i(n-i)} \] 很容易把\(2^{i(n-i)}\)拆成卷积形式,前面讲过,不再赘 ...
- python 中的pipe
from multiprocessing import Process,Queue,Pipe import os def f(q): # q.send([42,None,'hello']) print ...
- Java50道经典习题-程序20 求前20项之和
题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和.分析:请抓住分子与分母的变化规律.三个连续分数之间的规律是:上两个分子之和等于第三个分数的分子 ...
- docker-4-镜像
是什么 镜像是一种轻量级.可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件, 它包含运行某个软件所需的所有内容,包括代码.运行时.库.环境变量和配置文件. 1.UnionFS(联合文件 ...
- ethereumjs/merkle-patricia-tree-2-API
SecureTrie src/secure.js:10-15 Extends Trie 扩展前缀树 You can create a secure Trie where the keys are au ...