哈希表+哈希桶简介及实现
最近在学习通信协议,链路层传输时,对于报文的识别处理,建立哈希识别规则;之前简单学习过哈希,还没深入过,闲来无事把学习的内容通俗的表达出来,也和大家分享一下,让新入门的童鞋很快理解。
1、哈希表
我们都学过函数,三要素:自变量x,因变量y,对应关系f。
在函数里x、y通过映射关系f,一一对应起来。
那什么是哈希呢?
就是记录的储存位置和他的关键字之间建立一个确定的对应关系f,这里我们就可以吧这种对应关系f称之为哈希(Hash)函数。
那哈希表呢?
采用哈希函数将记录储存在一块连续的存储空间中,这块连续的储存空间就称之为哈希表(Hash table)
看到这你应该是明白定义了吧。
当然哈希表不仅可以用来储存,更是可以用来查找,效率是很高的,因为一一对应嘛,查找算法时间复杂度为O(1)!哈希表的建立的关键是哈希函数的构建,也就是如何形成key和value的对应。
理想情况当然是一一对应的关系最好,但是现实情况下,难免碰到f(key1)=f(key2),我们称之为冲突,key1同key2我们可以称为“同义词”所以构建哈希函数,处理冲突也就是很关键的事情了。
这里简单介绍几个建立哈希关系的方法,但不做深入探究,有兴趣的童鞋可以自行查找学习。
1.1直接定址法
f(key)=a*key+b
即取关键字的某个线性函数作为散列(哈希)地址;但我们需要知道关键字的分布,现实情况很少用。
1.2数字分析法
对于刚刚入学的童鞋都会有个学号,而前几位肯定相同,后面几位每个人都不同,这时(不用)分析便可知道,抽取后面几位,对这些数字进行操作,使之不相同,总而言之就是提供一个哈希函数,能够让关键字分配到哈希表中去。
1.3平方取中法
也就是说将数字平方,取中间几位作为关键字。
1.4除数取余法
假如你有n个数字
f(key)=key%p(p=<n)
也可平方后操作,当然这里关键在于找到合适的p
key | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
value | 12 | 25 | 38 | 15 | 16 | 29 | 78 | 67 | 56 | 21 | 22 | 47 |
这里p=12,正好一一对应,嘿嘿嘿。
1.5随机数法
f(key)=random(key)
就是用了random函数
额还有很多不写了。
然而事实情况是:构建这样的函数很难,所以要去解决这种冲突
也就是解决key1不等于key2,但f(key1)=f(key2)。
方法不少,列举一下吧,就不细说了,有兴趣的童鞋自行搜索学习。
1、开发定址法 2、再散列函数法 3、链地址法
2 哈希桶
实际上哈希桶是解决哈希表冲突的一种方法。常见的解决冲突的两种方法:1、分离链接法 2、开放定址法。其中使用分离链接法,得到的对应关系即为哈希桶。
哈希表中同一个位置可能存有多个元素,为应对哈希冲突问题,将哈希表中的每个位置表示一个哈希桶。
2.1哈希桶示意
每个key被hash(…)到相同的孔,那么如何解决冲突呢 就是在那个孔放一个链表
这个孔下面的链表就像桶一样盛着key对应的元素。
下面通过除数取余+分离链接画图简单说明下:
这里将冲突的元素插入到各桶头部,即链表头。
哈希桶就是盛放不同key链表的容器,在这里我们可以把每个key的位置看作是一个桶,桶里放了一个链表,故该方法也叫分离链接法。
另外,前面有说过,哈希表的查找算法时间复杂度是O(1)
,学过算法的同学,肯定知道哈希是以空间换取时间的,如果这里哈希通的key选择不当,复杂度将迅速升为O(n)
(当全部元素对应一个key值,退化为链表)
2.1哈希桶简单实现
代码很不难,学习的小伙伴耐心分析看看;我定义的节点里包括key和value
,实际上只需要一个值就可以生成对应哈希值;另外由于我定义时,用的数组,所以生成key是连续的,只是简单示例下。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define BUCKETCOUNT 10/*链表节点*/
struct Node{char* key;char* value;struct Node *next;
};typedef struct Node node;/*顺序哈希桶*/
struct hashTable{node bucket[BUCKETCOUNT];/*先默认定义10个桶*/
};typedef struct hashTable table;void initHashTable();
int hash(const char* key);
struct Node* lookup(const char* key);
int insertnode(table *t, char *key, char *value);
const char* get(const char* key);
void display();/*初始化哈希桶*/
void initHashTable(table *t){int i;if(!t) return;for(i = 0; i < BUCKETCOUNT; ++i){t->bucket[i].key = NULL;t->bucket[i].next = NULL;t->bucket[i].value = NULL;}
}/*哈希函数,根据生成哈希值*/
int hash(const char* key){int hash=0;for (; *key; ++key){hash=hash*33+*key;}return hash%BUCKETCOUNT;
}/*插入数据到hash表中*/
int insertnode(table *t,char *key,char *value){int index, vlen1, vlen2;node *e, *ep;if(t == NULL || key == NULL || value == NULL) return -1;index = hash(key);//如果现在桶为空,直接写入第一个桶节点if(!t->bucket[index].key){t->bucket[index].key = strdup(key);t->bucket[index].value = strdup(value);}else{e = ep = &(t->bucket[index]);/*先从已经存在的找*/while(e){/*如果key值重复,替换相应的值*/if(strcmp(e->key, key) == 0){vlen1 = strlen(value);vlen2 = strlen(e->value);if(vlen1 > vlen2){free(e->value);e->value = (char *)malloc(sizeof(vlen1 + 1));}memcpy(e->value, value, vlen1 + 1);return index;/*插入完成*/}ep = e;e = e->next;}/*没有在当前桶中找到,创建新的节点加入,并加入桶链表*/e = (node *)malloc(sizeof(node));if(NULL != e){e->key = strdup(key);e->value = strdup(value);e->next = NULL;ep->next = e;}}return index;
}/*打印hash表*/void display(table *t){int i;node *e;if(!t) return;for(i = 0; i < BUCKETCOUNT; i++){printf("bucket[%d]:", i);e = &(t->bucket[i]);while(e){printf("%s = %s\t\t",e->key, e->value);e = e->next;}printf("\n");}
}int main(int argc, char* argv[])
{table *ht;ht = (table *)malloc(sizeof(table));initHashTable(ht);char* key[]={"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};char* value[]={"apple","banana","china","doge","egg","fox","goat","hello","ice","jeep","key","love","mirror","net","orange","panda","quarter","rabbit","shark","tea","unix","volley","wolf","x-ray","yard","zoo"};for (int i = 0; i < 26; ++i){insertnode(ht, key[i], value[i]);}display(ht);return 0;
}
执行下看看效果:
PS:
函数**strdup()**
头文件: string.h
功 能: 将串拷贝到新建的位置处,strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。
返回值: 返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。
哈希表+哈希桶简介及实现相关推荐
- 哈希表 哈希函数 时间_您需要了解的哈希函数
哈希表 哈希函数 时间 安全从业人员的功能表中有一个工具可以帮助每个人理解,无论他们对计算机进行什么操作:加密哈希函数. 这听起来听起来像是神秘的,技术性的,甚至可能很无聊,但是我对什么是哈希以及它们 ...
- 数据结构与算法五:哈希表-哈希函数设计原则-哈希冲突解决方案
一.哈希表的定义: 二.哈希表举例: 哈希函数就是映射关系 三.哈希表应用举例: Leetcode上第387题: 思路:通过s.charAt(i)-'a'将字符串中的字符映射成hash表,出现一次,在 ...
- hash:哈希表 哈希桶
目录 1.哈希的思想 2.解决冲突 3.哈希表(采用的闭散列,线性探测) 4.哈希桶(开散列) 5.总结 1.哈希的思想 hash是hash是一种根据存储的关键字的哈希值确定存储位置的算法.哈希值通过 ...
- 初识哈希表——哈希表是个啥?
是什么? 哈希表是一种根据输入信息在已有数据库中快速查找匹配对于信息的算法. 为什么需要? 具体应用场景是,比如在图书管理系统中,用户输入一个图书名称,程序需要在整个图书数据库中找到对应的书籍.如果这 ...
- 哈希表哈希碰撞解决办法
哈希表: 散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构. 给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含 ...
- Leetcode 146. LRU缓存机制【哈希表 [哈希表存储每个元素在双向链表中的指针]+双向链表】
文章目录 问题描述 解题报告 实验代码 参考资料 问题描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . ...
- 【初阶与进阶C++详解】第十九篇:哈希(哈希函数+哈希冲突+哈希表+哈希桶)
- 14 哈希表和哈希桶
文章目录 一.哈希表 二.哈希函数 2.1. 直接定址法(常用) 2.2. 除留余数法(常用) 2.3. 几种不常用的方法 三.哈希冲突 四.闭散列 4.1. 线性探测 4.2. 负载因子 4.3. ...
- 哈希表(概念,冲突的解决,实现哈希桶)
目录 概念 冲突 如何尽量减少冲突? 负载因子 解决冲突的几种方案 冲突严重时的解决办法 哈希表的实现 基本类型哈希桶实现 泛型哈希桶实现 注意!!! 概念 构造出一种存储结构,通过某种函数使元素的存 ...
最新文章
- DotNetCore跨平台~dotnet pack打包详细介绍
- python3 函数签名简介 验证函数参数
- Bundle Identifier
- 用习惯了windows系统要怎样去认识linux系统(一)
- sql loader 参数详解
- mysql 从库可以写入吗_mysql主从库配置读写分离以及备份
- Vue-router学习(一)- 路由匹配
- LVS——DR模式下的健康检查
- 高效的css命名约定
- yum与apt-get的区别以及两者更新源(阿里/网易【163】)
- 根据服务端生成的WSDL文件创建客户端支持代码的三种方式
- 一分钟教你如何判断ip地址是外网还是内网
- 完全背包问题 c语言
- 吉林省注册公司流程:第一步 企业名称预先核准流程。
- 基于飞凌FETA40i-C核心板在光时域反射仪中的应用原理
- Prometheus监控服务器
- 向sdcard中添加文件出现Failed to push the item(s)
- 白酒知识丨酱香型白酒为何瓶子不透明?
- Java-web案例2(扩展了session与cookie)
- JAVA--GUI:MenuBar组件
热门文章
- mysql8修改密码报:Your password does not satisfy the current policy requirements
- vs2008编译QT开源项目--太阳神三国杀源码分析(一) 项目编译及整体分析
- 案例研究:设计令人震撼的名片!
- 数据和计算系统如何容错?
- win10怎么找工作组计算机,win10无法查看工作组的解决方法|win10系统怎么找工作组...
- 配置永久systemd日志
- Brave 安全隐私浏览器
- 我看到西电通院考试——学生应该做的事情?
- 瓜子二手车直卖网面试经历
- maven快速入门第十七讲——从私服下载jar包到本地仓库