哈希表(哈希函数和处理哈希冲突)

  1. 前言

关于哈希表的主题的小记原计划5月23日完成,由于本人新冠阳性,身体发烧乏力,周末感觉身体状况稍加恢复,赶紧打开电脑把本文完成,特别秉承“写是为了更好地思考,更好地思考才能取得更大进步”的思想,不敢懈怠,赶紧把学习到的东西记录下来。

首先我们要搞明白为什么要发明哈希表的技术?我们学习和讨论过各种结构(线性表、树、图等),数据记录在结构中的相对位置是随机的,记录和关键字之间不存在确定的关系,因此,在查找记录时,需进行和关键字进行一系列的比较。查找效率依赖于查找过程中所比较的次数。哈希表的发表就是希望不经过任何比较,一次存取便能得到查找记录,可以大大提升查找、插入、删除等基本操作的效率。

要实现哈希表的技术思想,我们必须在储存位置和它的关键字之间建立一个确定的对应关系或对应函数f,使每个关键字和数据结构的为转移储存位置相对应。因此在查找关键字key的时候,我们只需调取函数f(k),计算出数据的储存位置,若结构中存在的关键字和key相等,那么必定在f(key)的位置上。因此不需要进行任何比较便可直接取得查询的记录。在此我们称这个对应关系为哈希函数(Hash function),按这个思想建立的表称为哈希表(Hash table)。

  1. 哈希函数

2.1 哈希冲突

由于哈数是一类影像函数,那么它就比不可避免出现这样一类情况,原始的关键字key_m和key_n并不相等,但是经过哈希函数处理之后f(key_m)=f(key_n),这种情况下就出现所谓的“哈希冲突”。比如我们有5个关键字k1…k5,经过哈希函数h(x)影像之后,h(k2)=h(k5),那么就说k2和k5在h(x)哈希函数影像下出现冲突。

实际应用当中,只能减少哈希冲突的概率,而没有办法杜绝哈希冲突,因为哈希实际上是对关键字进行了某种程度信息上的压缩,导致的后果就是压缩的信息可能相同,需要再次进行区分。

2.2 哈希函数

构造哈希函数的方法有很多,在介绍各种方法之前,首先需要明确什么是好的哈希函数。若对关键字集合当中的任何一个关键字,经过哈希函数映射到地址中的任意地址的概率是相等的,则称作此类哈希函数是均匀的哈希函数。换句话手,就是经过哈希函数映射后,得到一个随机地址,以便使一组关键字的哈希地址分布在整个区间中,从而减少冲突。

2.3 构造哈希函数的常用方法

a) 直接地址法

取关键字或关键字某个线性函数的值为哈希地址,
H ( K e y ) = a ∗ k e y + b H(Key)=a*key+b H(Key)=a∗key+b
由于直接定址所得关键字地址和集合相同,对于不同的关键字不会发生哈希冲突,但在实际应用中,使用这种哈希哈数非常少。

b) 数字分析法

假设关键字都是以10为基的数,并且哈希表中的关键字都是事先知道的,则可以取关键字的若干位组成哈希地址。

c) 平方取中法

取关键字平方后的中间几位为哈希地址。这是一种较常用的构造哈希函数的方法。通常在选定哈希哈数的时候不一定能知道关键字的全部情况,取其中哪几位也不一定合适,而一个数字平方后的中间几位和数的每一位都相关,由此随机分布的关键字的哈希地址也是随机的。

d) 折叠法

将关键字分为位数相同的几部分,然后取这几个部分的叠加和作为哈希地址,这个方法称为折叠法(folding)。关键字位数很多,而且关键字每一位上数字分布大致均匀时,可以采用折叠法得到哈希地址。

e)除留余数法

取关键字被某个不大于表长m的数p除后所得余数为哈希地址,也即是说,
H ( k e y ) = k e y M O D p ( p ≤ m ) H(key)=key\ MOD\ p\ \ (p≤m) H(key)=key MOD p  (p≤m)
这是最简单,最常用的构造哈希函数的方法,不仅对关键字可以直接取模,也可在折叠,平方取中后进行取模处理。

  1. 哈希冲突处理方法

“好”的哈希函数可以减少哈希冲突概率,但不能避免,因此,如何处理哈希冲突是哈希造表不可缺少的一个方面。通常处理哈希冲突有下列几种方法:

3.1 开放定址法
H i = ( H ( k e y ) + d i ) M O D m ( i = 1 , 2 , . . . k ) , k < = ( m − 1 ) H_i=(H(key)+d_i)\ MOD\ m\ \ \ (i=1,2,...k), k<=(m-1) Hi​=(H(key)+di​) MOD m   (i=1,2,...k),k<=(m−1)
H(key)为哈希函数,m为哈希表长,di为增量序列,可用下列三种取法:

(1)di=1,2,3,…m-1, 线性探测

(2) di= ±12,±22,…±k2 称为二次探测再散列

(3) di= 伪随机序列

3.2 链地址法

将所有关键字为同义词的记录存储在同一线性表中。假定某哈希函数产生的哈希地址在区间[0,m-1]上,则设立一个指针型向量

Chain ChainHash[m]

其每个分量的初始地址都是空指针,凡是哈希地址为i的记录,都插入到头指针为ChainHash[i]的链表中。为了便于查找、插入和删除操作,需要保证后续链表按关键字有序。

  1. 哈希函数实现

本文采用链地址方法处理冲突,哈希函数采用最简单的除留余数法。

4.1 头文件

/*** @file hash_table.h* @author your name (you@domain.com)* @brief * @version 0.1* @date 2023-05-23* * @copyright Copyright (c) 2023* */
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
#include "../00_introduction/Status.h"#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))#define MAX_LEN 20#define m 13    //采用除留余数法,除数定义为13typedef int    KeyType;
typedef char*  Record;typedef struct  ElemType
{KeyType key;Record  value;
}ElemType;typedef struct HashNode
{KeyType key;struct HashNode *next;
} HashNode, *HashPtr;typedef enum NodeType
{head,intermediate
}NodeType;typedef struct Result
{NodeType type;HashPtr *node_ptr;  //待返回的节点HashPtr *node_ptr_2; //待返回节点的前一节点,主要用于删除操作int      flag; // 0= search failure, 1 = search success
} Result;typedef struct HashTable
{ElemType   *elem;        //Use the dynamic programming to allocate the spaceint        count;        //Number of element in the current hashtableint        size_index;   //Capacity of hash table
} HashTable;typedef struct SSTable
{ElemType *elem;int len;
} SSTable;/*** @brief Create a static table* * @param fp File to pointer* @param st Static table*/
void create_table(FILE *fp, SSTable *st);/*** @brief Intialize the HashPtr as Null pointer** @param hash_chain* @param m Number of pointer of hash*/
void init_hash(HashPtr *hash_chain);/*** @brief Use hash function to map to the address* 采用取余的方法* @param key Key value* @return int Return hash address*/
int hash_function(KeyType key);/*** @brief search 'key' from the hash table* * @param ht Hash table variable* @param key Key value* @param p   Position of key value* @param c   Number of collision in the search* @return Result */
Result search_hash(HashPtr *hash, KeyType key);/*** @brief Insert one element into the hashtable** @param hash Pointer to hash table* @param key Element type* @return Status -Return success or unsuccess*/
Status insert_hash(HashPtr *hash, KeyType key);/*** @brief Insert one element into the hashtable** @param hash Pointer to hash table* @param key Element type* @return Status -Return success or unsuccess*/
Status delete_hash(HashPtr *hash, KeyType key);#endif

4.2 函数实现

/*** @file hash_table.c* @author your name (you@domain.com)* @brief * @version 0.1* @date 2023-05-23* * @copyright Copyright (c) 2023* */
#ifndef HASH_TABLE_C
#define HASH_TABLE_C
#include "hash_table.h"void create_table(FILE *fp, SSTable *st)
{int n;char str[MAX_LEN];int i;n=0;// 当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。while(fgets(str,MAX_LEN,fp)!=NULL){n++;}fseek(fp,0,SEEK_SET);st->len=n;st->elem=(ElemType *)malloc(sizeof(ElemType)*(n+1));for(i=1;i<=n;i++){st->elem[i].value=(Record)malloc(sizeof(char)*MAX_LEN);memset(st->elem[i].value,0,sizeof(char)*MAX_LEN);fscanf(fp,"%d %s",&(st->elem[i].key),st->elem[i].value);}return;
}void init_hash(HashPtr *hash_chain)
{int i;for(i=0;i<m;i++){*(hash_chain+i)=NULL;}return;
}int hash_function(KeyType key)
{return (key%m);
}Result search_hash(HashPtr *hash, KeyType key)
{Result res;int  k;HashPtr *p;HashPtr *pre_p;bool termination=false;bool found =false;p = (HashPtr *)malloc(sizeof(HashPtr));pre_p = (HashPtr *)malloc(sizeof(HashPtr));k=hash_function(key);if(hash[k]==NULL || ((hash[k]!=NULL) && key<= hash[k]->key)){res.flag=0;res.type=head;res.node_ptr=hash+k;if ((hash[k] != NULL) && key == hash[k]->key){res.flag=1;}return res;}*p=hash[k];*pre_p=NULL;while ((*p) && !termination){if((*p)->key==key){termination = true;found =true;}else if (key < (*p)->key){termination =true;}else{*pre_p=*p;*p=(*p)->next;}}if(found){res.flag=1;res.type=intermediate;res.node_ptr=p;res.node_ptr_2=pre_p;return res;}else{res.flag = 0;res.type = intermediate;res.node_ptr = pre_p;res.node_ptr_2=p;return res;}
}Status insert_hash(HashPtr *hash, KeyType key)
{Result res;HashPtr new_node;HashPtr temp;res=search_hash(hash,key);if(res.flag==1){return ERROR;}else{new_node=(HashPtr)malloc(sizeof(HashNode));new_node->key=key;new_node->next=NULL;if(res.type==head){new_node->next=*(res.node_ptr);*(res.node_ptr)=new_node;}else{new_node->next = (*(res.node_ptr))->next;(*(res.node_ptr))->next = new_node;}}return OK;
}Status delete_hash(HashPtr *hash, KeyType key)
{Result res;HashPtr temp;res = search_hash(hash, key);if (res.flag == 0){return ERROR;}else{if (res.type == head){(*(res.node_ptr)) = (*(res.node_ptr))->next;}else The previous next will be the current next{(*(res.node_ptr_2))->next = (*(res.node_ptr))->next; }}
}#endif

4.3测试函数

/*** @file hash_table_main.c* @author your name (you@domain.com)* @brief * @version 0.1* @date 2023-05-21* * @copyright Copyright (c) 2023* */
#ifndef HASH_TABLE_MAIN_C
#define HASH_TABLE_MAIN_C
#include "hash_table.c"int main(void)
{int i;FILE *fp;SSTable st;HashPtr hash[m];fp=fopen("data.txt","r");create_table(fp,&st);init_hash(hash);for(i=1;i<=st.len;i++){insert_hash(hash,st.elem[i].key);}delete_hash(hash,14);printf("This is the end of test\n");getchar();fclose(fp);return EXIT_SUCCESS;
}#endif
  1. 小结

本文对哈希表、哈希函数以及处理哈希冲突的方法进行总结,并且利用C语言对实现了简单的哈希插入、查找和删除操作。

参考资料:

《数据结构》清华大学,严蔚敏

哈希表(哈希函数和处理哈希冲突)_20230528相关推荐

  1. 算法练习day12——190331(哈希函数、哈希表、布隆过滤器、一致性哈希)

    1.哈希函数 1.1 特点: 经典的哈希函数输入域是无穷大的. 输出域是有穷尽的: 相同输入得到的输出肯定是一样的: 不同的输入得到的输出也可能一样(输入域>输出域); 哈希函数的离散型:给定多 ...

  2. 哈希表数据结构_算法与数据结构-哈希表

    前面我们已经讲到了数组和链表,数组能通过下标 O(1) 访问,但是删除一个中间元素却要移动其他元素,时间 O(n). 循环双端链表倒是可以在知道一个节点的情况下迅速删除它,但是吧查找又成了 O(n). ...

  3. 【编程学习】浅谈哈希表及用C语言构建哈希表!

    哈希表:通过key-value而直接进行访问的数据结构,不用经过关键值间的比较,从而省去了大量处理时间. 哈希函数:选择的最主要考虑因素--尽可能避免冲突的出现 构造哈希函数的原则是: ①函数本身便于 ...

  4. C++(数据结构与算法):30---散列(哈希)表的介绍(散列函数、散列冲突、散列溢出)

    一.散列(哈希)介绍 散列使用一个散列函数(也称为哈希函数)把字典的数对映射到一个散列表(也称为哈希表)的具体位置 散列的存储与查找: 查找:如果数对p的关键字是k,散列函数为f,那么在理想的情况下, ...

  5. 哈希表(模拟散列表 字符串哈希)

    目录 一.哈希表的概念 二.模拟散列表 题目 代码实现 ①拉链法 ②开放寻址法 三.字符串哈希 题目 思路 注意点 代码实现 一.哈希表的概念 哈希表(又称为散列表),将一个比较大的值域映射到一个小的 ...

  6. 转载一篇《Redis源码研究—哈希表》重点是如何重新哈希

    <Redis源码研究-哈希表>来自:董的博客 网址:http://dongxicheng.org/nosql/redis-code-hashtable/ 转载于:https://www.c ...

  7. c语言哈希表电子辞典_C语言实现的哈希表实现程序

    下面我们一起来看一个C语言实现的哈希表实现程序了,这个程序没有过多的说明只是一个例子,大家有兴趣可以进入看看或测试一下. 为了巩固一下链表知识,自己实现的一个哈希表,在GCC 4.4.7下编译通过: ...

  8. 哈希表 matlab实现,MATLAB中的哈希表

    MATLAB中的哈希表 MATLAB是否支持散列表? 一些背景 我正在研究Matlab中的一个问题,需要图像的尺度空间表示. 为了做到这一点,我创build了一个二维高斯滤波器,在一定范围内为方差si ...

  9. 实现哈希表 java,如何实现Java的哈希表?

    Does anyone know how Java implements its hash tables (HashSet or HashMap)? Given the various types o ...

  10. c语言散列表的构造和查找,简单的哈希表实现 C语言

    简单的哈希表实现 这是一个简单的哈希表的实现,用c语言做的. 原理 先说一下原理. 先是有一个bucket数组,也就是所谓的桶. 哈希表的特点就是数据与其在表中的位置存在相关性,也就是有关系的,通过数 ...

最新文章

  1. 最小系统必须安装的组件(仅做参考)
  2. Centos配置nginx反向代理8080端口到80端口
  3. linux共享库位置配置(LD_LIBRARY_PATH环境变量 或者 更改/etc/ld.so.conf)
  4. 【流媒体服务器的搭建】2. 源码编译安装ffmpeg
  5. 攻防世界reverse新手练习
  6. 如何看待夸克,酷狗概念版等简洁型软件?
  7. XP中的重要惯例和规则
  8. 一个完整的网络工程项目【很难得找到的 相当详细完整】
  9. hbase的region分区
  10. matlab matconvnet
  11. 杭州电子科技大学acm--2005
  12. 考研从机械到计算机难吗,考研机械真的不行吗?
  13. 八爪鱼爬取列表数据和详情页数据(国内网址)
  14. 10大免费视频素材网站
  15. 已拿头条offer的研发工程师面经(C++)
  16. VirtualBox安装教程和ubuntu16导入
  17. android自动连接到指定wifi
  18. 【python】批量实现modis数据的辐射定标,大气校正及地形校正
  19. 2021年升降机司机考试试卷及升降机司机考试试题
  20. idea模块加载失败

热门文章

  1. pytorch 报错“THCudaCheck FAIL file=/pytorch/aten/src/THC/THCGeneral.cpp line=663 error=11“解决方案
  2. bzoj2716-天使玩偶
  3. 关于BufferedReader的read()及readLine()
  4. 点击网页上的手机号码直接拨打电话
  5. 发展区块链技术 打造竞争新优势
  6. 产品经理常见的概念:蝴蝶效应、马太效应
  7. (12)使用depends-on
  8. 电子烟的软件开发架构及欧美测试标准+中国标准+多国标准
  9. 学海无涯 回头是岸……
  10. win10设置里找不到以太网