简单的哈希表实现

这是一个简单的哈希表的实现,用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语言相关推荐

  1. 用c语言写的电话簿的程序,用C语言散列表实现电话薄

    #用C语言散列表实现电话薄# 标题 #include#include#includetypedef struct node { char num[11],name[15],address[20],ci ...

  2. 用C语言散列表实现电话薄

    #用C语言散列表实现电话薄# 标题 #include<stdio.h> #include<string.h> #include<stdlib.h> typedef ...

  3. 查找算法【哈希表】 - 散列查找及性能分析

    查找算法[哈希表] - 散列查找及性能分析 散列表虽然建立了关键字和存储位置之间的直接映像,但冲突不可避免,在散列表的查找过程中,有的关键字可以通过直接定址1次比较找到,有的关键字可能仍然需要和若干关 ...

  4. 查找算法【哈希表】 - 处理冲突的方法:开放地址法-线性探测法

    查找算法[哈希表] - 处理冲突的方法 无论如何设计散列函数,都无法避免发生冲突. 如果发生冲突,就需要处理冲突. 处理冲突的方法分为3种: 开放地址法 链地址法 建立公共溢出区. [开放地址法] 开 ...

  5. LeetCode刷题——哈希表(python语言)

    LeetCode刷题--哈希表(python语言) 一.哈希表 1.1 哈希表的概念 哈希表,也叫散列表.其实可以很像python的字典,也就是键(key)值(Hash(key))对,最简单也最常用的 ...

  6. 查找算法【哈希表】- 散列函数

    查找算法[哈希表]- 散列函数 散列函数(Hash Function),又被称为哈希函数,是将关键字映射到存储地址的函数,被记为hash(key)=Addr. 设计散列函数时需要遵循两个原则: ①散列 ...

  7. C语言实现简单的哈希表

    CRC是通信领域中用于校验数据传输正确性的最常用机制,也是Hash算法的一个典型应用,Hash一般翻译为"散列",也可直接音译为"哈希",就是把任意长度的输入( ...

  8. Java中的查找树和哈希表(一级)

    下面我们来看一下JAVA中有哪些查找树和哈希表,我们分两块内容来讲呗,第一块我们首先来讲查找树,第二块我们来讲哈希表,JAVA里面我们有一个TreeSet,还有一个TreeMap,他们底层都是使用了红 ...

  9. (2)散列表是怎么进行查找的

    散列过程 整个散列过程其实就是两步. 1. 在存储的时候,通过散列函数计算记录的散列地址,并按此散列地址存储该记录. 就像张三丰我们就让他在体育馆,那如果是"爱因斯坦"我们让他在图 ...

最新文章

  1. JAVA图片处理--缩放,切割,类型转换
  2. Android -- 多线程下载
  3. JedisConnectionException: java.net.SocketException: Broken pipe
  4. [状态压缩DP] COJ 1129 送货到家
  5. 如何成为一名合格的数据分析师
  6. 数据库周刊 | DBA 核心技能
  7. UOJ#386. 【UNR #3】鸽子固定器(链表)
  8. java培训 lambda表达式_java 8 中lambda表达式学习
  9. 华为交换机关闭网口_关闭端口的命令 怎么开启华为交换机关闭端口,命令谁知道啊。...
  10. 实践篇(四):Apache jena SPARQL endpoint及推理
  11. 《画解数据结构》九张动图,画解队列
  12. 计算机二级软件java_全国计算机等级考试使用的java软件版本
  13. 《Java语言高级特性(阿里云大学)》笔记 第7~14章 类库+正则+国际化+比较器(文档+思维导图)
  14. 乖离率背离公式_股价偏离率是什么 BIAS指标计算公式-BIAS-技术指标-股票入门基础知识学习网...
  15. NRF51822——LCD128X64驱动
  16. 机器学习算法(十二):聚类
  17. 【IDE-Visual Studio】VC\VS2005\VS2008\VS2010 消除 忽略 加强警告Warning
  18. CUDA10 下载百度云 cuDnn下载(Windows版 )
  19. iphone相册储存空间已满_苹果手机存储空间满了怎么办?
  20. 08【matplotlib】06matplotlib绘制多次图形和不同图形的差异介绍和总结

热门文章

  1. 校安行 |“熊孩子”出没无影踪?暑期安全防范不能忽视
  2. Python 头文件
  3. 小米6X恰恰讽刺了雷军只要5%的净利润
  4. 关于“无人直播”看这一篇就够了
  5. 干货 | 快速搞懂DC-DC升压电路和DC-DC升压模块原理
  6. 搜索在线看电影的方法
  7. 希腊棺材之谜——复盘
  8. 爬取2016年世界500强的数据,进行分析
  9. 总资产=净资产+负债,负债当然是资产
  10. Servlet功能实现和执行过程、servlet的生命周期、Servlet线程安全问题、Servlet的三种映射方式、ServletConfig、ServletContext