哈希表:通过key-value而直接进行访问的数据结构,不用经过关键值间的比较,从而省去了大量处理时间。

哈希函数:选择的最主要考虑因素——尽可能避免冲突的出现

构造哈希函数的原则是:

函数本身便于计算;

计算出来的地址分布均匀,即对任一关键字k,f(k) 对应不同地址的概率相等,目的是尽可能减少冲突。

1.直接定址法:

如果我们现在要对0-100岁的人口数字统计表,那么我们对年龄这个关键字就可以直接用年龄的数字作为地址。此时f(key) = key。

这个时候,我们可以得出这么个哈希函数:f(0) = 0,f(1) = 1,……,f(20) = 20。这个是根据我们自己设定的直接定址来的。人数我们可以不管,我们关心的是如何通过关键字找到地址。

如果我们现在要统计的是80后出生年份的人口数,那么我们对出生年份这个关键字可以用年份减去1980来作为地址。此时f (key) = key-1980。

假如今年是2000年,那么1980年出生的人就是20岁了,此时 f(2000) = 2000 - 1980,可以找得到地址20,地址20里保存了数据“人数500万”。

也就是说,我们可以取关键字的某个线性函数值为散列地址,即:f(key) = a × key + b

这样的散列函数优点就是简单、均匀,也不会产生冲突,但问题是这需要事先知道关键字的分布情况,适合査找表较小且连续的情况。由于这样的限制,在现实应用中,直接定址法虽然简单,但却并不常用。

2.数字分析法:

分析一组数据,比如一组员工的出生年月日,这时我们发现出生年月日的前几位数字大体相同,这样的话,出现冲突的几率就会很大;

但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果用后面的数字来构成散列地址,则冲突的几率会明显降低。因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。

3.折叠法:

将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址。

4.除留余数法:

(常用:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p, p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。

5. 平方取中法

当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。

6. 伪随机数法:

采用一个伪随机函数做哈希函数,即h(key)=random(key)。

解决冲突方法

● 开放定址法:

当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。这个过程可用下式描述: H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1)) 其中: H ( key ) 为关键字 key 的直接哈希地址, m 为哈希表的长度, di 为每次再探测时的地址增量。

采用这种方法时,首先计算出元素的直接哈希地址 H ( key ) ,如果该存储单元已被其他元素占用,则继续查看地址为 H ( key ) + d 2 的存储单元,如此重复直至找到某个存储单元为空时,将关键字为 key 的数据元素存放到该单元。

增量 d 可以有不同的取法,并根据其取法有不同的称呼:

( 1 ) d i = 1 , 2 , 3 , …… 线性探测再散列;

( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2…… 二次探测再散列;

( 3 ) d i = 伪随机序列 伪随机再散列;

● 链地址法:

链地址法解决冲突的做法是:如果哈希表空间为 0 ~ m - 1 ,设置一个由 m 个指针分量组成的一维数组 ST[ m ], 凡哈希地址为 i 的数据元素都插入到头指针为 ST[ i ] 的链表中。这种方法有点近似于邻接表的基本思想,且这种方法适合于冲突比较严重的情况。

● 公共溢出区法:

将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

C语言实现

       定义一些宏与结构体

#define HashMaxSize 1000 //哈希表最大容量#define LoadFactor 0.8  //负载因子,表示哈希表的负载能力typedef int KeyType;typedef int ValueType;typedef size_t(*HashFunc)(KeyType key)//定义HashFunc是一个指向函数的指定,它可以指向函数类型有size_t且有一个int参数的函数;重定义哈希函数typedef enum Stat{  //表示每个元素的状态Empty,  //空,当前没有值Valid,  //当前的值有效Invalid //非空但无效,表示当前节点被删除}Stat;typedef struct HashElem    //哈希表的元素结构体{/* data */KeyType key;ValueType value;Stat stat;}HashElem;typedef struct HashTable    //哈希表{HashElem data[HashMaxSize];size_t size;    //当前有效的元素个数HashFunc hashfunc;}HashTable;

       函数声明

void HashTableInit(HashTable *ht,HashFunc hashfunc);//初始化哈希表int HashTableInsert(HashTable *ht,KeyType key,ValueType value);int HashTableFind(HashTable *ht,KeyType key,ValueType *value,size_t *cur);//哈希表的查找,找到返回1,并返回这个节点的value值,未找到返回0void HashRemove(HashTable *ht,KeyType key);//删除值为key的结点int HashEmpty(HashTable *ht);//判断哈希表是否为空size_t HashSize(HashTable *ht);//求哈希表的大小void HashTableDestroy(HashTable *ht);//销毁哈希表

       函数实现

size_t HashFuncDefault(KeyType key){return key%HashMaxSize;//检查是否超出最大储存量}void HashTableInit(HashTable *ht){if (ht == NULL) //非法输入return;ht->size = 0;ht->hashfunc = HashFuncDefault;//错误赋值for (size_t i =0;i<HashMaxSize;i++){ht->data[i].key = 0;ht->dota[i].stat = Empty;ht->data[i].value = 0;}}int HashTableInsert(HashTable *ht,KeyType key,ValueType value){if (ht == NULL)return 0;if (ht->size >= HashMaxSize*LoadFactor) //当哈希表的size超出了负载return 0;size_t cur = ht->hashfunc(key); //根据哈希函数将key转换,求得key在哈希表中的下标while(1){//判断当前下标是否被占用if (ht->data[cur].key ==key) //保证不会用重复的数字存入哈希表return 0;if(ht->data[cur].stat != Valid){ht->data[cur].key =key;ht->data[cur].value = value;ht->data[cur].stat = Valid;ht->size++;return 1;}cur++;}}int HashTableFind(HashTable *ht,KeyType key,ValueType *value){//哈希表的查找,找到返回1,没找到返回0if(ht == NULL)return 0;size_t offset=ht->hashfunc(key);//通过哈希函数找到key的下标if(ht->data[offset].key == key && ht->data[offset].stat == Valid){//若当前下标所对应的值正好是key并且当前的状态必须为valid才返回*value = ht->data[offset].value;return 1;} else{//值不为key,根据冲突解决方法查找(当前解决方法为逐个往后查),直到找到stat等于emptywhile (ht->data[offset].stat !=Empty){if(ht->data[offset].key !=key){offset++;if(offset >= HashMaxSize//判断下标是否超出最大值)offset = 0;} else{if(ht->data[offset].stat == Valid){*value =ht->data[offset].value;return 1;} elseoffset++;}}return 0;}}int HashTableFindCur(HashTable *ht,KeyType key,size_t *cur){//删除节点if(ht == NULL)return 0;for(size_t i = 0;i < HashMaxSize;i++){if(ht->data[i].key == key && ht->data[i].stat == Valid){*cur = i;//?return 1;}}return 0;}void HashRemove(HashTable *ht,KeyType key){if (ht == NULL) //非法输入return;ValueType value = 0;size_t cur =0;int ret = HashTableFindCur(ht,key,&cur);//通过find函数得到key是否存在哈希表中if(ret == 0)return;else{ht->data[cur].stat = Invalid;ht->size--;}}int HashEmpty(HashTable *ht){if(ht == NULL)return 0;elsereturn ht->size >0?1:0}size_t HashSize(HashTable *ht){//求哈希表大小if(ht == NULL) {ht->data->stat = Empty;return ht->size;} else{return ht->size;}}void HashPrint(HashTable *ht, const char *msg){if(ht ==NULL || ht->size = 0)return;printf("%s/n",msg);for(size_t i =0;i< HashMaxSize;i++){if(ht->data[i],stat != Empty)printf("[%d] key=%d value=%d stat =%d\n",i,ht->data[i].key,ht->data[i].value,ht->data[i].stat);}}

— END —
看到这里你是不是又学到了很多新知识呢~

如果你很想学编程,小编推荐我的C语言/C++编程学习基地【点击进入】!

都是学编程小伙伴们,带你入个门还是简简单单啦,一起学习,一起加油~

还有许多学习资料和视频,相信你会喜欢的!

涉及:游戏开发、常用软件开发、编程基础知识、课程设计、黑客等等......

【编程学习】浅谈哈希表及用C语言构建哈希表!相关推荐

  1. php 如何设计索引_Mysql学习浅谈mysql的索引设计原则以及常见索引的区别

    <Mysql学习浅谈mysql的索引设计原则以及常见索引的区别>要点: 本文介绍了Mysql学习浅谈mysql的索引设计原则以及常见索引的区别,希望对您有用.如果有疑问,可以联系我们. 索 ...

  2. 读我们的学科——计算机专业学习浅谈

    读我们的学科--计算机专业学习浅谈 (北理计算机学院新闻社<九歌>专栏文章) 作者:林健 一.广泛了解,从科普书籍开始 选择计算机专业的同学,也许是因为原先有一定的基础,也许是因为一时的激 ...

  3. oracle全表扫过程讲解,CSS_浅谈存取Oracle当中扫描数据的方法,1) 全表扫描(Full Table Scans, FTS) - phpStudy...

    浅谈存取Oracle当中扫描数据的方法 1) 全表扫描(Full Table Scans, FTS) 为实现全表扫描,Oracle读取表中所有的行,并检查每一行是否满足语句的WHERE限制条件一个多块 ...

  4. python采用函数式编程模式-浅谈Python 函数式编程

    匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...

  5. AI+教育 I 69天流利说APP学习浅谈自适应学习

    本文梳理了目前"AI+教育"场景的典型产品,然后基于我在英语流利说APP学习的"懂你英语"."地道发音2.0"等课程的学习体验,对自适应学习 ...

  6. python实例编程_浅谈如何编程Python3——Python实例(3)

    浅谈如何编程Python3--Python实例(3) # 测试实例一 print("测试实例一") str= "runoob.com"print(str.isa ...

  7. 量子计算机编程pdf,浅谈量子计算与编程.pdf

    浅谈量子计算与编程 OSDT 2017 邢明杰 2017-10-21 量子计算 "Changes occurring to a quantum state can be described ...

  8. java和python混合编程_浅谈C++与Java混合编程

    在学习编程的过程中, 我觉得不止要获得课本的知识, 更多的是通过学习技术知识提高解决问题的能力, 这样我们才能走在最前方, 更 多 Java 学习,请登陆疯狂 java 官网. 现实的情况是, 真实的 ...

  9. c语言乘法表 m*(9-i),C语言做九九乘法表.doc

    C语言做九九乘法表 #include void main() { int i,j,x; /*第一种*/ printf("第一种:\n"); for(i=1;i<=9;i++) ...

最新文章

  1. SpringBoot 修改banner信息
  2. 清空数据库错误:因为该表正由 FOREIGN KEY 约束引用 解决办法
  3. rjdbc读取mysql_R通过RJDBC连接外部数据库 (转)
  4. java数字格式化_Java数字格式
  5. htonl(), ntohl(), htons(), ntohs() 函数
  6. android 入门 (分析: 非匿名内部类 监听功能的实现)
  7. ajax hash缓存
  8. word转PDF(使用liberOffice插件)
  9. 根据汉字自动生成拼音
  10. ccs 动态梦幻西游
  11. python plc fx5u_三菱FX5U可编程控制器硬件及指令篇
  12. vmware 提示该虚拟机正在使用中
  13. AI视觉传感器作用和应用介绍
  14. 在键盘上输入数n,编程计算sum=1!+2!+··· + n!的结果
  15. 使用代理服务器导致电脑无法打开网页解决办法
  16. 使用HTTPie测试Web服务
  17. 【项目实战】Python实现多元线性回归模型(statsmodels OLS算法)项目实战
  18. mac maven 配置。简单四步
  19. Flash CS4 过期了
  20. 《开源项目系列》 --- 简阅

热门文章

  1. tensorflow出现报错: Could not locate zlibwapi.dll或者Could not load library cudnn_cnn_infer64_8.dll.
  2. html中 a链接的默认样式,超链接的默认样式
  3. 【机器学习】多项式回归案例五:正则惩罚解决过拟合(Ridge回归和Lasso回归)
  4. Core Data概述
  5. Charles的安装和注册码破解
  6. springSecurity 中不能抛出异常UserNameNotFoundException 解析
  7. 用Python爬取手机APP
  8. 转“软件测试职业发展方向”
  9. 网络攻击肆虐-给你的网络设备来套防弹衣吧!
  10. 设计模式学习系列6 原型模式(prototype)