C语言实现的数据结构之------哈希表

原文链接:https://blog.csdn.net/smstong/article/details/51145786

哈希表原理

这里不讲高深理论,只说直观感受。哈希表的目的就是为了根据数据的部分内容(关键字),直接计算出存放完整数据的内存地址。

试想一下,如果从链表中根据关键字查找一个元素,那么就需要遍历才能得到这个元素的内存地址,如果链表长度很大,查找就需要更多的时间.

void* list_find_by_key(list,key)
{for(p=list;p!=NULL; p=p->next){if(p->key == key){return p;}return p;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

为了解决根据关键字快速找到元素的存放地址,哈希表应运而生。它通过某种算法(哈希函数)直接根据关键字计算出元素的存放地址,由于无需遍历,所以效率很高。

void* hash_table_find_by_key(table, key)
{void* p = hash(key);return p;
}
  • 1
  • 2
  • 3
  • 4
  • 5

当然,上面的伪代码忽略了一个重要的事实:那就是不同的关键字可能产生出同样的hash值。

hash("张三") = 23;
hash("李四") = 30;
hash("王五") = 23;
  • 1
  • 2
  • 3

这种情况称为“冲突”,为了解决这个问题,有两种方法:一是链式扩展;二是开放寻址。这里只讲第一种:链式扩展。

也就是把具有相同hash值的元素放到一起,形成一个链表。这样在插入和寻找数据的时候就需要进一步判断。

void* hash_table_find_by_key(table, key)
{void* list = hash(key);return list_find_by_key(list, key);
}
  • 1
  • 2
  • 3
  • 4
  • 5

需要注意的是,只要hash函数合适,这里的链表通常都长度不大,所以查找效率依然很高。

下图是一个哈希表运行时内存布局:

![这里写图片描述](https://img-blog.csdn.net/20160413191511085)

2 纯C实现源码

实际工作中,大多数情况下,关键字都是字符串的形式,而大多数教科书上却使用整数关键字来举例,这非常脱离实际。为此,本人决定使用纯C语言开发一个哈希表结构,供大家参考。主要特点:

  • 基于接口开发,对外彻底隐藏实现细节
  • 具有自动释放客户结构内存的回调功能
  • 采用经典的Times33哈希算法
  • 采用纯C开发,可供C和C++客户使用

HashTable.h 头文件

#pragma once
typedef struct HashTable HashTable;#ifdef __cplusplus
extern "C" {
#endif/* new an instance of HashTable */HashTable* hash_table_new();/*delete an instance of HashTable,all values are removed auotmatically.*/void hash_table_delete(HashTable* ht);/*add or update a value to ht, free_value(if not NULL) is called automatically when the value is removed.return 0 if success, -1 if error occurred.*/#define hash_table_put(ht,key,value) hash_table_put2(ht,key,value,NULL);int hash_table_put2(HashTable* ht, char* key, void* value, void(*free_value)(void*));/* get a value indexed by key, return NULL if not found. */void* hash_table_get(HashTable* ht, char* key);/* remove a value indexed by key */void hash_table_rm(HashTable* ht, char* key);#ifdef __cplusplus
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

HashTable.c 实现文件

#include "HashTable.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>#define TABLE_SIZE (1024*1024)/* element of the hash table's chain list */
struct kv
{struct kv* next;char* key;void* value;void(*free_value)(void*);
};/* HashTable */
struct HashTable
{struct kv ** table;
};/* constructor of struct kv */
static void init_kv(struct kv* kv)
{kv->next = NULL;kv->key = NULL;kv->value = NULL;kv->free_value = NULL;
}
/* destructor of struct kv */
static void free_kv(struct kv* kv)
{if (kv) {if (kv->free_value) {kv->free_value(kv->value);}free(kv->key);kv->key = NULL;free(kv);}
}
/* the classic Times33 hash function */
static unsigned int hash_33(char* key)
{unsigned int hash = 0;while (*key) {hash = (hash << 5) + hash + *key++;}return hash;
}/* new a HashTable instance */
HashTable* hash_table_new()
{HashTable* ht = malloc(sizeof(HashTable));if (NULL == ht) {hash_table_delete(ht);return NULL;}ht->table = malloc(sizeof(struct kv*) * TABLE_SIZE);if (NULL == ht->table) {hash_table_delete(ht);return NULL;}memset(ht->table, 0, sizeof(struct kv*) * TABLE_SIZE);return ht;
}
/* delete a HashTable instance */
void hash_table_delete(HashTable* ht)
{if (ht) {if (ht->table) {int i = 0;for (i = 0; i<TABLE_SIZE; i++) {struct kv* p = ht->table[i];struct kv* q = NULL;while (p) {q = p->next;free_kv(p);p = q;}}free(ht->table);ht->table = NULL;}free(ht);}
}/* insert or update a value indexed by key */
int hash_table_put2(HashTable* ht, char* key, void* value, void(*free_value)(void*))
{int i = hash_33(key) % TABLE_SIZE;struct kv* p = ht->table[i];struct kv* prep = p;while (p) { /* if key is already stroed, update its value */if (strcmp(p->key, key) == 0) {if (p->free_value) {p->free_value(p->value);}p->value = value;p->free_value = free_value;break;}prep = p;p = p->next;}if (p == NULL) {/* if key has not been stored, then add it */char* kstr = malloc(strlen(key) + 1);if (kstr == NULL) {return -1;}struct kv * kv = malloc(sizeof(struct kv));if (NULL == kv) {free(kstr);kstr = NULL;return -1;}init_kv(kv);kv->next = NULL;strcpy(kstr, key);kv->key = kstr;kv->value = value;kv->free_value = free_value;if (prep == NULL) {ht->table[i] = kv;}else {prep->next = kv;}}return 0;
}/* get a value indexed by key */
void* hash_table_get(HashTable* ht, char* key)
{int i = hash_33(key) % TABLE_SIZE;struct kv* p = ht->table[i];while (p) {if (strcmp(key, p->key) == 0) {return p->value;}p = p->next;}return NULL;
}/* remove a value indexed by key */
void hash_table_rm(HashTable* ht, char* key)
{int i = hash_33(key) % TABLE_SIZE;struct kv* p = ht->table[i];struct kv* prep = p;while (p) {if (strcmp(key, p->key) == 0) {free_kv(p);if (p == prep) {ht->table[i] = NULL;}else {prep->next = p->next;}}prep = p;p = p->next;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174

3 测试程序

下面是测试程序源码,基于C++。

测试程序test.cpp

#include <stdio.h>
#include <stdlib.h>#include "HashTable.h"// 要放入哈希表中的结构体
struct Student
{int age;float score;char name[32];char data[1024 * 1024* 10];
};// 结构体内存释放函数
static void free_student(void* stu)
{free(stu);
}// 显示学生信息的函数
static void show_student(struct Student* p)
{printf("姓名:%s, 年龄:%d, 学分:%.2f\n", p->name, p->age, p->score);
}int main()
{// 新建一个HashTable实例HashTable* ht = hash_table_new();if (NULL == ht) {return -1;}// 向哈希表中加入多个学生结构体for (int i = 0; i < 100; i++) {struct Student * stu = (struct Student*)malloc(sizeof(struct Student));stu->age = 18 + rand()%5;stu->score = 50.0f + rand() % 100;sprintf(stu->name, "同学%d", i);hash_table_put2(ht, stu->name, stu, free_student);}// 根据学生姓名查找学生结构for (int i = 0; i < 100; i++) {char name[32];sprintf(name, "同学%d", i);struct Student * stu = (struct Student*)hash_table_get(ht, name);show_student(stu);}// 销毁哈希表实例hash_table_delete(ht);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
                <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e44c3c0e64.css" rel="stylesheet"></div>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文浅尝 | EARL: Joint Entity and Relation Linking for QA over KG

Mohnish Dubey, Debayan Banerjee, Debanjan Chaudhuri, Jens Lehmann: EARL: Joint Entity and Relation Linking for Question Answering over Knowledge Graphs. International Semantic Web Conference (1) 2018: 108-126链接&#xff1a;https://link.springer.com/conten…

百度高级Java三面题目!涵盖JVM +Java锁+分布式等

百度高级Java一面 自我介绍 对象相等的判断&#xff0c;equals方法实现。 Hashcode的作用&#xff0c;与 equal 有什么区别&#xff1f; Java中CAS算法&#xff1f; G1回收器讲一下&#xff1f; HashMap&#xff0c;ConcurrentHashMap与LinkedHashMap的区别 如何在多线程环…

python--从入门到实践--chapter 12 pygame_Alien_Invasion

安装pygame包&#xff0c;把安装好的包copy一份到pycharm工程目录下&#xff0c;不然找不到pygame包 抄一遍书上的代码&#xff1a; settings.py class Settings():def __init__(self):self.screen_width 1200self.screen_height 800self.bg_color (255, 255, 255)self.s…

实时事理学习与搜索平台DemoV1.0正式对外发布

我们团队探索了一种将事件、概念、逻辑、实时学习、多类知识库实时更新串起来的知识服务新模式。一个面向事理的实时学习和搜索系统Demo&#xff0c;取名叫“学迹”&#xff0c;取自“学事理&#xff0c;知行迹”。 项目地址&#xff1a;https://xueji.zhiwenben.com 一、 “学…

卖萌屋算法工程师思维导图part3—深度学习篇

卖萌屋的妹子们&#xff08;划掉&#xff09;作者团整理的算法工程师思维导图&#xff0c;求职/自我提升/查漏补缺神器。该手册一共分为数据结构与算法、数学基础、统计机器学习和深度学习四个部分。下面是第三部分深度学习的内容~公众号后台回复【思维导图】获取完整手册&…

1.3 字符串的全排列

字符串的全排列 题目描述&#xff1a;输入一个字符串&#xff0c;打印出该字符串中字符的所有排列。例如&#xff0c;输入字符串“abc”&#xff0c;则输出由字符‘a’&#xff0c;‘b’&#xff0c;‘c’所能排列出来的所有字符串“abc”&#xff0c;“acb”&#xff0c;“bac…

论文浅尝 | Zero-Shot Transfer Learning for Event Extraction

事件抽取的目标是在非结构化的文本中确认事件的触发词&#xff08;Eventtrigger&#xff09;和参与者&#xff08;Event argument&#xff09;&#xff0c;并判断触法词的事件类型&#xff08;Eventtype&#xff09;&#xff0c;判断参与者在事件中的扮演的角色&#xff08;Arg…

今日头条Java后台Java研发三面题目

最近有同学在优知学院留言区留言是否能发布今日头条的面试题目&#xff0c;这位同学&#xff0c;题目来了哦~ 一面 concurrent包下面用过哪些&#xff1f; countdownlatch功能实现 synchronized和lock区别&#xff0c;重入锁 thread和runnable的区别 AtomicInteger实现原理…

实时事理逻辑知识库(事理图谱)终身学习项目-EventKGNELL(学迹)

EventKGNELL EventKGNELL, event knowlege graph never end learning system, a event-centric knowledge base search system&#xff0c;实时事理逻辑知识库终身学习和事件为核心的知识库搜索项目。包括事件概念抽取、事件因果逻辑抽取、事件数据关联推荐与推理。 项目地址&…

python--从入门到实践--chapter 15 16 17 生成数据/下载数据/web API

1.随机漫步 random_walk.py from random import choice class RandomWalk():def __init__(self, num_points5000):self.num_points num_pointsself.x_value [0]self.y_value [0]def fill_walk(self):while len(self.x_value) < self.num_points:x_direction choice([1…

写文章 银行国企技术岗面经+总结:适合自己的才是最好的

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/37842198 银行&国企技术岗面经总结&#xff1a;适合自己的才是最好的本人某985计算机水硕&#xff0c;技术水平非常一般。自去年暑假找工作以来&#xff0c;经历了互联网、银行、国企等各类企业的招聘&#xff0c;最终进…

ACL2020 | 基于Knowledge Embedding的多跳知识图谱问答

一只小狐狸带你解锁 炼丹术&NLP 秘籍作者&#xff1a;舒意恒&#xff08;南京大学硕士生&#xff0c;知识图谱方向&#xff09;背景什么是知识图谱问答&#xff1f;知识图谱&#xff08;KG&#xff09;是一个多关系图&#xff0c;其中包含数以百万计的实体&#xff0c;以及…

论文浅尝 | 基于超平面的时间感知知识图谱嵌入

链接&#xff1a;http://talukdar.net/papers/emnlp2018_HyTE.pdf本文主要关注 KG embedding 中三元组成立的时间有效性问题&#xff0c;比如三元组(Cristiano Ronaldo, playsFor, Manchester United)&#xff0c;其成立的有效时间段是2003年到2009年&#xff0c;这个使三元组有…

Java面试进阶:Dubbo、Zookeeper面试题锦集

Dubbo面试题锦集 1、默认也推荐使用netty框架&#xff0c;还有mina。 2、默认是阻塞的&#xff0c;可以异步调用&#xff0c;没有返回值的可以这么做。 3、推荐使用zookeeper注册中心&#xff0c;还有redis等不推荐。 4、默认使用Hessian序列化&#xff0c;还有Duddo、FastJ…

如何看待事理图谱版magi--学迹

如何看待事理图谱版magi–“学迹”:项目地址&#xff1a;https://xueji.zhiwenben.com 看到界面和功能都似乎很相似&#xff0c;除了magi更通用一些&#xff0c;这个系统更专注“事件”这个领域&#xff0c;请问两者的区别是什么&#xff1f;或者说这个系统就是利用magi背后的…

POJ 1064 分割线缆(二分查找)

题目链接&#xff1a;http://poj.org/problem?id1064 题目大意&#xff1a;多根电缆切成指定段数&#xff08;每段相同长度&#xff09;&#xff0c;求每段线缆的最大长度&#xff08;精确到0.01&#xff09; 这题精度控制是难点&#xff0c;方法很简单&#xff0c;二分查找…

Learning to rank基本算法小结

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/26539920 Learning to rank基本算法小结最近工作中需要调研一下搜索排序相关的方法&#xff0c;这里写一篇水文&#xff0c;总结一下几天下来的调研成果。包括Learning to rank 基本方法Learning to rank 指标介绍LambdaMART…

命名实体识别难在哪?

亚里士多德在《形而上学》中认为&#xff0c;对于存在&#xff0c;最重要的问题&#xff0c;就是给世间万物的存在基于语言来分层和分类。从神说要有光起&#xff0c;到基友给你取了个外号叫狗蛋。你会发现&#xff0c;创造与命名&#xff0c;在历史中往往等同。名字是自我概念…

论文浅尝 | 面向简单知识库问答的模式修正强化策略

链接&#xff1a;http://aclweb.org/anthology/C18-1277知识库问答研究旨在利用结构化事实回答自然语言问题&#xff0c;在网络中&#xff0c;简单问题占据了相当大的比例。本文提出在完成模式抽取和实体链接后&#xff0c;构建一个模式修正机制&#xff0c;从而缓解错误积累问…

最全BAT数据库面试89题:mysql、大数据、redis

数据库 mysql面试题目&#xff1a; MySQL InnoDB、Mysaim的特点&#xff1f; 乐观锁和悲观锁的区别&#xff1f;&#xff1f; 行锁和表锁的区别&#xff1f; 数据库隔离级别是什么&#xff1f;有什么作用&#xff1f; MySQL主备同步的基本原理。 如何优化数据库性能&#…