简单的哈希表实现 C语言
简单的哈希表实现
这是一个简单的哈希表的实现,用c
语言做的。
原理
先说一下原理。
先是有一个bucket
数组,也就是所谓的桶。
哈希表的特点就是数据
与其在表中的位置存在相关性
,也就是有关系的,通过数据应该可以计算出其位置。
这个哈希表是用于存储一些键值对(key -- value
)关系的数据,其key
也就是其在表中的索引,value
是附带的数据。
通过散列算法,将字符串的key
映射到某个桶中,这个算法是确定的,也就是说一个key
必然对应一个bucket
。
然后是碰撞问题,也就是说多个key
对应一个索引值。举个例子:有三个key
:key1
,key3
,key5
通过散列算法keyToIndex
得到的索引值都为2
,也就是这三个key
产生了碰撞,对于碰撞的处理,采取的是用链表连接起来,而没有进行再散列。
这是包含的头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #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 = ; 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 = ; i<BUCKETCOUNT; ++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 -; len = strlen(key);
index = (int)key[];
for (i = ; i<len; ++i) {
index *= + (int)key[i];
}
index >>= ;
index &= (BUCKETCOUNT - );
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 + );
if (ret != NULL) {
memcpy(ret , str , len);
ret[len] = '\0';
}
return ret;
}
string.h
中的相关函数
#include <string.h> 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 -;
} 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) == ) {
//找到key所在,替换值
vlen1 = strlen(value);
vlen2 = strlen(e->value);
if (vlen1 > vlen2) {
free(e->value);
e->value = (char*)malloc(vlen1 + );
}
memcpy(e->value , value , vlen1 + );
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 ( == 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 ( == 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 = ; i<BUCKETCOUNT; ++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 = ; i < ; ++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 ;
}
简单的哈希表实现 C语言的更多相关文章
- C语言实现简单的哈希表
这是一个简单的哈希表的实现,用c语言做的. 哈希表原理 这里不讲高深理论,只说直观感受.哈希表的目的就是为了根据数据的部分内容(关键字),直接计算出存放完整数据的内存地址. 试想一下,如果从链表中根据 ...
- 数据结构---哈希表的C语言实现(闭散列)
原文地址:https://blog.csdn.net/weixin_40331034/article/details/79461705 构造一种存储结构,通过某种函数(hashFunc)使元素的存储位 ...
- 哈希表的C语言实现
首先介绍一下什么是哈希表.同线性表.树一样,哈希表也是一种数据结构,理想情况下可以不需要任何比较,一次存取便能得到所查记录.所以它的优点就是查找特定记录的速度快.因为哈希表是基于数组的,所以创建后就难 ...
- [数据结构 - 第8章] 查找之哈希表(C语言实现)
首先是需要定义一个哈希表的结构以及一些相关的常数.其中 HashTable 就是哈希表结构.结构当中的 elem 为一个动态数组. #define HASHSIZE 12 // 定义哈希表长为数组的长 ...
- C语言-简单哈希表(hash table)
腾讯三面的时候,叫我写了个哈希表,当时紧张没写好···结果跪了··· 回来后粪发涂墙,赶紧写了一个! 什么都不说了···先让我到厕所里面哭一会··· %>_<% 果然现场发挥,以及基础扎实 ...
- 数据结构和算法(Golang实现)(26)查找算法-哈希表
哈希表:散列查找 一.线性查找 我们要通过一个键key来查找相应的值value.有一种最简单的方式,就是将键值对存放在链表里,然后遍历链表来查找是否存在key,存在则更新键对应的值,不存在则将键值对链 ...
- [CareerCup] 8.10 Implement a Hash Table 实现一个哈希表
8.10 Design and implement a hash table which uses chaining (linked lists) to handle collisions. 这道题让 ...
- php扩展开发-实现一个简易的哈希表
从一个简易的哈希表入手,会让你更好的理解php的哈希表,他们的本质是一样的,只是php的哈希表做了更多的功能扩展,php的哈希表是php语言的一个重要核心,大量的内核代码使用到哈希表. #includ ...
- Python实现哈希表(分离链接法)
一.python实现哈希表 只使用list,构建简单的哈希表(字典对象) # 不使用字典构造的分离连接法版哈希表 class HashList(): """ Simple ...
随机推荐
- Java——其他容器
除了JFrame表示之外,还有其他几种常见的窗体:JPanel.JSplitPane.JTabbedPane.JScrollPane.JDesktopPane.JInternalFrame等. imp ...
- svn 强制用户添加注释 和 允许用户修改注释
当我们用TortoiseSVN提交代码时,有很多人不喜欢写注释,导致以后代码版本多,也不清楚哪个版本到底改了什么东西.所以在提交的时候,我会强制要求添加注释.这是如何实现的?这个话题就涉及到了svn的 ...
- window.onscroll页面滚动条滚动事件
用途一:"返回顶部": window.onscroll = function(){ var t = document.documentElement.scrollTop || do ...
- Robot Framework--12 RFS+AutoItLibrary测试web对话框
转自:http://blog.csdn.net/tulituqi/article/details/21871247 Selenium2library在我们实际测试web页面的时候基本上已经够用了,不过 ...
- The method below converts an array of objects to a DataTable object in C#.
http://www.c-sharpcorner.com/blogs/dynamic-objects-conveting-into-data-table-in-c-sharp1 public stat ...
- Linux 挂载 NFS
NFS(网络文件系统),这是在 Linux 系统上常用的文件共享方式.也可以做为作为一个远程存储使用,比如:我有个网站,用户可以上传文件,但文件慢慢会越来越多,这个时候我们只能把存放上传文件的目录挂在 ...
- 巧用svn create patch(打补丁)方案解决定制版需求
最近项目定制版越来越多,维护,同步代码非常费事.以前的思路如下图: 以前的svn目录结构如下图: 这样问题有2个: 若在一个定制包中修复了其他定制包也有的bug,同步更新其他包的代码时,非常费劲+机械 ...
- Criteria 和 DetachedCriteria的区别与使用
Criteria 和 DetachedCriteria 的主要区别在于创建的形式不一样, Criteria 是在线的,所以它是由 Hibernate Session 进行创建的:而 DetachedC ...
- mysql数据库存储过程异常处理
14.1.4 定义条件和处理程序 定义条件和处理程序是事先定义程序执行过程中可能遇到的问题.并且可以在处理程序中定义解决这些问题的办法.这种方式可以提前预测可能出现的问题, 并提出解决办法.这样可以 ...
- 10个基础的linux网络和监控命令
配置zookeeper集群时,需要查看本机ip,输入命令 hostname -i 就会只显示主机ip, 下边搜了一篇常用的 命令,闲的时候多敲敲命令,以便用的时候再找! 我下面列出来的10个 ...