c语言散列表的构造和查找,简单的哈希表实现 C语言
简单的哈希表实现
这是一个简单的哈希表的实现,用c语言做的。
原理
先说一下原理。
先是有一个bucket数组,也就是所谓的桶。
哈希表的特点就是数据与其在表中的位置存在相关性,也就是有关系的,通过数据应该可以计算出其位置。
这个哈希表是用于存储一些键值对(key -- value)关系的数据,其key也就是其在表中的索引,value是附带的数据。
通过散列算法,将字符串的key映射到某个桶中,这个算法是确定的,也就是说一个key必然对应一个bucket。
然后是碰撞问题,也就是说多个key对应一个索引值。举个例子:有三个key:key1,key3,key5通过散列算法keyToIndex得到的索引值都为2,也就是这三个key产生了碰撞,对于碰撞的处理,采取的是用链表连接起来,而没有进行再散列。
这是包含的头文件
#include
#include
#include
#define BUCKETCOUNT 16
哈希表和节点数据结构的定义
struct hashEntry
{
const char* key;
char* value;
struct hashEntry* next;
};
typedef struct hashEntry entry;
struct hashTable
{
entry bucket[BUCKETCOUNT]; //先默认定义16个桶
};
typedef struct hashTable table;
初始化和释放哈希表
//初始化哈希表
void initHashTable(table* t)
{
int i;
if (t == NULL)return;
for (i = 0; i < BUCKETCOUNT; ++i) {
t->bucket[i].key = NULL;
t->bucket[i].value = NULL;
t->bucket[i].next = NULL;
}
}
//释放哈希表
void freeHashTable(table* t)
{
int i;
entry* e,*ep;
if (t == NULL)return;
for (i = 0; i
e = &(t->bucket[i]);
while (e->next != NULL) {
ep = e->next;
e->next = ep->next;
free(ep->key);
free(ep->value);
free(ep);
}
}
}
哈希散列算法
//哈希散列方法函数
int keyToIndex(const char* key)
{
int index , len , i;
if (key == NULL)return -1;
len = strlen(key);
index = (int)key[0];
for (i = 1; i
index *= 1103515245 + (int)key[i];
}
index >>= 27;
index &= (BUCKETCOUNT - 1);
return index;
}
辅助函数strDup
这是比较多余的做法,因为C标准库中string.h中有一系列这样的函数。
//在堆上分配足以保存str的内存
//并拷贝str内容到新分配位置
char* strDup(const char* str)
{
int len;
char* ret;
if (str == NULL)return NULL;
len = strlen(str);
ret = (char*)malloc(len + 1);
if (ret != NULL) {
memcpy(ret , str , len);
ret[len] = '\0';
}
return ret;
}
string.h中的相关函数
#include
char *strdup(const char *s);
char *strndup(const char *s, size_t n);
char *strdupa(const char *s);
char *strndupa(const char *s, size_t n);
哈希表的插入和修改
这个了插入和修改是一个方法,如果key在哈希表中已经存在,那么就是修改value,否则就是插入一个节点。
//向哈希表中插入数据
int insertEntry(table* t , const char* key , const char* value)
{
int index , vlen1 , vlen2;
entry* e , *ep;
if (t == NULL || key == NULL || value == NULL) {
return -1;
}
index = keyToIndex(key);
if (t->bucket[index].key == NULL) {
t->bucket[index].key = strDup(key);
t->bucket[index].value = strDup(value);
}
else {
e = ep = &(t->bucket[index]);
while (e != NULL) { //先从已有的找
if (strcmp(e->key , key) == 0) {
//找到key所在,替换值
vlen1 = strlen(value);
vlen2 = strlen(e->value);
if (vlen1 > vlen2) {
free(e->value);
e->value = (char*)malloc(vlen1 + 1);
}
memcpy(e->value , value , vlen1 + 1);
return index; //插入完成了
}
ep = e;
e = e->next;
} // end while(e...
//没有在当前桶中找到
//创建条目加入
e = (entry*)malloc(sizeof (entry));
e->key = strDup(key);
e->value = strDup(value);
e->next = NULL;
ep->next = e;
}
return index;
}
哈希表中查找
因为这个哈希表中保存的是键值对,所以这个方法是从哈希表中查找key对应的value的。要注意,这里返回的是value的地址,不应该对其指向的数据进行修改,否则可能会有意外发生。
//在哈希表中查找key对应的value
//找到了返回value的地址,没找到返回NULL
const char* findValueByKey(const table* t , const char* key)
{
int index;
const entry* e;
if (t == NULL || key == NULL) {
return NULL;
}
index = keyToIndex(key);
e = &(t->bucket[index]);
if (e->key == NULL) return NULL;//这个桶还没有元素
while (e != NULL) {
if (0 == strcmp(key , e->key)) {
return e->value; //找到了,返回值
}
e = e->next;
}
return NULL;
}
哈希表元素的移除
这个函数用于将哈希表中key对应的节点移除,如果其不存在,那就返回NULL。如果存在,就返回这个节点的地址。注意,这里并没有释放节点,如果不需要了,应该手动释放它。
//在哈希表中查找key对应的entry
//找到了返回entry,并将其从哈希表中移除
//没找到返回NULL
entry* removeEntry(table* t , char* key)
{
int index;
entry* e,*ep; //查找的时候,把ep作为返回值
if (t == NULL || key == NULL) {
return NULL;
}
index = keyToIndex(key);
e = &(t->bucket[index]);
while (e != NULL) {
if (0 == strcmp(key , e->key)) {
//如果是桶的第一个
if (e == &(t->bucket[index])) {
//如果这个桶有两个或以上元素
//交换第一个和第二个,然后移除第二个
ep = e->next;
if (ep != NULL) {
entry tmp = *e; //做浅拷贝交换
*e = *ep;//相当于链表的头节点已经移除
*ep = tmp; //这就是移除下来的链表头节点
ep->next = NULL;
}
else {//这个桶只有第一个元素
ep = (entry*)malloc(sizeof(entry));
*ep = *e;
e->key = e->value = NULL;
e->next = NULL;
}
}
else {
//如果不是桶的第一个元素
//找到它的前一个(这是前面设计不佳导致的多余操作)
ep = &(t->bucket[index]);
while (ep->next != e)ep = ep->next;
//将e从中拿出来
ep->next = e->next;
e->next = NULL;
ep = e;
}
return ep;
}// end if(strcmp...
e = e->next;
}
return NULL;
}
哈希表打印
这个函数用于打印哈希表的内容的。
void printTable(table* t)
{
int i;
entry* e;
if (t == NULL)return;
for (i = 0; i
printf("\nbucket[%d]:\n" , i);
e = &(t->bucket[i]);
while (e->key != NULL) {
printf("\t%s\t=\t%s\n" , e->key , e->value);
if (e->next == NULL)break;
e = e->next;
}
}
}
测试一下
用于测试的数据来自于本机相关信息。
int main()
{
table t;
initHashTable(&t);
insertEntry(&t , "电脑型号" , "华硕 X550JK 笔记本电脑");
insertEntry(&t , "操作系统" , "Windows 8.1 64位 (DirectX 11)");
insertEntry(&t , "处理器" , "英特尔 Core i7 - 4710HQ @ 2.50GHz 四核");
insertEntry(&t , "主板" , "华硕 X550JK(英特尔 Haswell)");
insertEntry(&t , "内存" , "4 GB(Hynix / Hyundai)");
insertEntry(&t , "主硬盘" , "日立 HGST HTS541010A9E680(1 TB / 5400 转 / 分)");
insertEntry(&t , "显卡" , "NVIDIA GeForce GTX 850M (2 GB / 华硕)");
insertEntry(&t , "显示器" , "奇美 CMN15C4(15.3 英寸)");
insertEntry(&t , "光驱" , "松下 DVD - RAM UJ8E2 S DVD刻录机");
insertEntry(&t , "声卡" , "Conexant SmartAudio HD @ 英特尔 Lynx Point 高保真音频");
insertEntry(&t , "网卡" , "瑞昱 RTL8168 / 8111 / 8112 Gigabit Ethernet Controller / 华硕");
insertEntry(&t , "主板型号" , "华硕 X550JK");
insertEntry(&t , "芯片组" , "英特尔 Haswell");
insertEntry(&t , "BIOS" , "X550JK.301");
insertEntry(&t , "制造日期" , "06 / 26 / 2014");
insertEntry(&t , "主人" , "就是我");
insertEntry(&t , "价格" , "六十张红色毛主席");
insertEntry(&t , "主硬盘" , "换了个120G的固态");
entry* e = removeEntry(&t , "主板型号");
if (e != NULL) {
puts("找到后要释放");
free(e->key);
free(e->value);
free(e);
e = NULL;
}
printTable(&t);
const char* keys[] = { "显示器" , "主人","没有" , "处理器" };
for (int i = 0; i < 4; ++i) {
const char* value = findValueByKey(&t , keys[i]);
if (value != NULL) {
printf("find %s\t=\t%s\n" ,keys[i], value);
}
else {
printf("not found %s\n",keys[i]);
}
}
freeHashTable(&t);
getchar();
return 0;
}
c语言散列表的构造和查找,简单的哈希表实现 C语言相关推荐
- 用c语言写的电话簿的程序,用C语言散列表实现电话薄
#用C语言散列表实现电话薄# 标题 #include#include#includetypedef struct node { char num[11],name[15],address[20],ci ...
- 用C语言散列表实现电话薄
#用C语言散列表实现电话薄# 标题 #include<stdio.h> #include<string.h> #include<stdlib.h> typedef ...
- 查找算法【哈希表】 - 散列查找及性能分析
查找算法[哈希表] - 散列查找及性能分析 散列表虽然建立了关键字和存储位置之间的直接映像,但冲突不可避免,在散列表的查找过程中,有的关键字可以通过直接定址1次比较找到,有的关键字可能仍然需要和若干关 ...
- 查找算法【哈希表】 - 处理冲突的方法:开放地址法-线性探测法
查找算法[哈希表] - 处理冲突的方法 无论如何设计散列函数,都无法避免发生冲突. 如果发生冲突,就需要处理冲突. 处理冲突的方法分为3种: 开放地址法 链地址法 建立公共溢出区. [开放地址法] 开 ...
- LeetCode刷题——哈希表(python语言)
LeetCode刷题--哈希表(python语言) 一.哈希表 1.1 哈希表的概念 哈希表,也叫散列表.其实可以很像python的字典,也就是键(key)值(Hash(key))对,最简单也最常用的 ...
- 查找算法【哈希表】- 散列函数
查找算法[哈希表]- 散列函数 散列函数(Hash Function),又被称为哈希函数,是将关键字映射到存储地址的函数,被记为hash(key)=Addr. 设计散列函数时需要遵循两个原则: ①散列 ...
- C语言实现简单的哈希表
CRC是通信领域中用于校验数据传输正确性的最常用机制,也是Hash算法的一个典型应用,Hash一般翻译为"散列",也可直接音译为"哈希",就是把任意长度的输入( ...
- Java中的查找树和哈希表(一级)
下面我们来看一下JAVA中有哪些查找树和哈希表,我们分两块内容来讲呗,第一块我们首先来讲查找树,第二块我们来讲哈希表,JAVA里面我们有一个TreeSet,还有一个TreeMap,他们底层都是使用了红 ...
- (2)散列表是怎么进行查找的
散列过程 整个散列过程其实就是两步. 1. 在存储的时候,通过散列函数计算记录的散列地址,并按此散列地址存储该记录. 就像张三丰我们就让他在体育馆,那如果是"爱因斯坦"我们让他在图 ...
最新文章
- JAVA图片处理--缩放,切割,类型转换
- Android -- 多线程下载
- JedisConnectionException: java.net.SocketException: Broken pipe
- [状态压缩DP] COJ 1129 送货到家
- 如何成为一名合格的数据分析师
- 数据库周刊 | DBA 核心技能
- UOJ#386. 【UNR #3】鸽子固定器(链表)
- java培训 lambda表达式_java 8 中lambda表达式学习
- 华为交换机关闭网口_关闭端口的命令 怎么开启华为交换机关闭端口,命令谁知道啊。...
- 实践篇(四):Apache jena SPARQL endpoint及推理
- 《画解数据结构》九张动图,画解队列
- 计算机二级软件java_全国计算机等级考试使用的java软件版本
- 《Java语言高级特性(阿里云大学)》笔记 第7~14章 类库+正则+国际化+比较器(文档+思维导图)
- 乖离率背离公式_股价偏离率是什么 BIAS指标计算公式-BIAS-技术指标-股票入门基础知识学习网...
- NRF51822——LCD128X64驱动
- 机器学习算法(十二):聚类
- 【IDE-Visual Studio】VC\VS2005\VS2008\VS2010 消除 忽略 加强警告Warning
- CUDA10 下载百度云 cuDnn下载(Windows版 )
- iphone相册储存空间已满_苹果手机存储空间满了怎么办?
- 08【matplotlib】06matplotlib绘制多次图形和不同图形的差异介绍和总结
热门文章
- 校安行 |“熊孩子”出没无影踪?暑期安全防范不能忽视
- Python 头文件
- 小米6X恰恰讽刺了雷军只要5%的净利润
- 关于“无人直播”看这一篇就够了
- 干货 | 快速搞懂DC-DC升压电路和DC-DC升压模块原理
- 搜索在线看电影的方法
- 希腊棺材之谜——复盘
- 爬取2016年世界500强的数据,进行分析
- 总资产=净资产+负债,负债当然是资产
- Servlet功能实现和执行过程、servlet的生命周期、Servlet线程安全问题、Servlet的三种映射方式、ServletConfig、ServletContext