基础概念

散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key).这里对应关系f称为散列函数,又称为哈希(Hash)函数。

采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。

散列技术既是一种存储方法,也是一种查找方法。

散列技术最适合的求解问题是查找与给定值相等的记录。不适合一对多的查找,也不适合范围查找。

散列技术中的两个关键问题:

设计一个简单、均匀、存储利用率高的散列函数; 冲突

散列查找中,时常会碰到两个关键字key1!=key2,但是却有f(key1)==f(key2),这种现象称为冲突(collision),并把key1和key2称为这个散列函数的同义词(synonym)。

几种散列函数的构造方法

直接定址法

取关键字的某个线性函数值为散列地址,即

f(key)=a×key+b           (a,b为常数)

优点:简单、均匀、不会产生冲突;

确定:需事先知道关键字的分布情况,适合查找表比较小且连续的情况。

不常用。

数字分析法

抽取关键字的一部分来计算散列位置的方法,也可以对抽取出来的数字再进行反转,循环移位,甚至前两数与后两数叠加等方法。适合处理关键字比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀,就可以考虑用这个方法。

平方取中法

例如:关键字1234,其平方值为1522756,取其中间三位就是227,用作散列地址。

平方取中适用于不知道关键字的分布,而位数又不是很大的情况。

折叠法

折叠法是将关键字从左到右分割成位数相等的几部分(注意最后一部分位数不够时可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。

例如:关键字9876543210,散列表表长为三位,我们将它分为四组,987|654|321|0,然后将他们叠加求和987+654+321+0=1962,再求后3位得到散列地址为962.

折叠法事先不需要知道关键字的分布,适合关键字位数较多的情况。

除留余数法

此法为最常用的构造散列函数方法,对于散列表长为m的散列函数公式为:

f(key) = key mod p       (p<=m)

根据经验,若散列表表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。

随机数法

取关键字的随机函数值为它的散列地址,f(key)=random(key).

当关键字的长度不等时,采用这个方法构造散列函数是比较合适的。

设计散列函数需要考虑的因素:

计算散列地址所需的事件 关键字的长度 散列表的大小 关键字的分布情况 记录查找的频率

处理散列冲突的方法

开放定址法

开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入.公式:

fi(key)=(f(key)+di) MOD m     (di=1,2,3,……,m-1)

这种解决冲突的开放定址法称为线性探测法。

当表中第i,i+1,i+2位置上已填有记录时,下一哈希地址为i,i+1,i+2和i+3的记录都将填入i+3的位置。这种第一个哈希地址不同的记录争夺同一个后继哈希地址的现象成为“聚集”(堆积)。 堆积使得需要不断处理冲突。可以改进di=1,-1,4,-4,9,-9,……,q*q,-q*q,(q<=m/2);正负号相间等于是可以双向寻找可能的空位置,增加平方运算的目的是为了不让关键字都聚集在某一块区域,这种方法称为二次探测法。

另外,在冲突时,对于位移量di可以采用伪随机函数计算得到,这种方法称之为随即探测法。

再散列函数法

事先准备多个散列函数

fi(key) = RHi(key)       (i=1,2,…k)

这里RHi就是不同的散列函数,每当发生散列地址冲突时,就换一个散列函数计算,相信总会有一个可以把冲突解决掉。

链地址法

将所有关键字为同义词的记录存储在一个单链表中,这种表称为同义词子表,在散列表中只存储所有同义词子表的头指针。

特点
(1)不产生“聚集”现象,故ASL较短;
(2)结点空间动态申请,适合于表前无法确定表长的情况;
(3)删除结点的操作易于实现;
(4)α=n/m较大时,所用空间比开放地址法多。但α越大,
开放地址法所需探查次数越多。

公共溢出区法

将所有冲突的关键字发那个在一个公共的溢出区来存放,当散列查找不成功时,则到溢出表去进行顺序查找。

散列表查找算法实现

/*代码源自《大话数据结构》一书*/
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0#define MAXSIZE 100 /* 存储空间初始分配量 */#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12 /* 定义散列表长为数组的长度 */
#define NULLKEY -32768 typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef struct
{int *elem; /* 数据元素存储基址,动态分配数组 */int count; /*  当前数据元素个数 */
}HashTable;int m=0; /* 散列表表长,全局变量 *//* 初始化散列表 */
Status InitHashTable(HashTable *H)
{int i;m=HASHSIZE;H->count=m;H->elem=(int *)malloc(m*sizeof(int));for(i=0;i<m;i++)H->elem[i]=NULLKEY; return OK;
}/* 散列函数 */
int Hash(int key)
{return key % m; /* 除留余数法 */
}/* 插入关键字进散列表 */
void InsertHash(HashTable *H,int key)
{int addr = Hash(key); /* 求散列地址 */while (H->elem[addr] != NULLKEY) /* 如果不为空,则冲突 */{addr = (addr+1) % m; /* 开放定址法的线性探测 */}H->elem[addr] = key; /* 直到有空位后插入关键字 */
}/* 散列表查找关键字 */
Status SearchHash(HashTable H,int key,int *addr)
{*addr = Hash(key);  /* 求散列地址 */while(H.elem[*addr] != key) /* 如果不为空,则冲突 */{*addr = (*addr+1) % m; /* 开放定址法的线性探测 */if (H.elem[*addr] == NULLKEY || *addr == Hash(key)) /* 如果循环回到原点 */return UNSUCCESS;    /* 则说明关键字不存在 */}return SUCCESS;
}int main()
{int arr[HASHSIZE]={12,67,56,16,25,37,22,29,15,47,48,34};int i,p,key,result;HashTable H;key=39;InitHashTable(&H);for(i=0;i<m;i++)InsertHash(&H,arr[i]);result=SearchHash(H,key,&p);if (result)printf("查找 %d 的地址为:%d \n",key,p);elseprintf("查找 %d 失败。\n",key);for(i=0;i<m;i++){key=arr[i];SearchHash(H,key,&p);printf("查找 %d 的地址为:%d \n",key,p);}return 0;
}

散列表查找性能分析

如果没有冲突,散列查找的时间复杂度为O(1).

影响散列查找的平均查找长度因素

1.散列函数是否均匀

2.处理冲突的方法

3.散列表的装填因子

所谓装填因子α=填入表中的记录个数/散列表长度。α越大,产生冲突的可能性就越大。

转载于:https://www.cnblogs.com/xingchenfeng/p/3714783.html

查找 之 散列表查找(哈希表)相关推荐

  1. 【Java】 大话数据结构(13) 查找算法(4) (散列表(哈希表))

    本文根据<大话数据结构>一书,实现了Java版的一个简单的散列表(哈希表). 基本概念 对关键字key,将其值存放在f(key)的存储位置上.由此,在查找时不需比较,只需计算出f(key) ...

  2. 散列查找 散列表(哈希表)

    哈希表的平均查找长度是()的函数. A.哈希表的长度 B.哈希表的装填因子 C.哈希函数 D.表中元素的多少 装填因子 = 关键字个数 / 表长 符号表:是 "名字(Name)–属性(Att ...

  3. [数据结构] 散列表(哈希表)

    散列表(哈希表) 比较难理解的官方定义:散列表/哈希表(Hash table),是根据关键码值(Key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来访问记录,以加快查找的 ...

  4. 什么是散列表(哈希表)?

    散列表(哈希表) 概念 散列表的构造方法 处理冲突的方法 后续 概念 散列函数:一个把查找表中的关键字映射成该关键字对应的地址的函数,记为Hash(key)=Addr(这里的地址可以是数组下标.索引或 ...

  5. (1)散列表(哈希表)的定义

    一般的查找 给你一个顺序表,你会如何查找某个给定的元素? 一般思路就是从表头开始,一个挨一个的比较记录a[i]与key的值是"="还是"≠",直到有相等才算是査 ...

  6. 散列表(哈希表)知识详解

    哈希表 1. 散列表查找(哈希表)概述 2.散列函数的构造方法 3.处理散列(哈希)冲突的方法 3.1开放定址法 3.2 再散列函数法 3.3 链地址法 3.4 公共溢出区法 4. 散列表查找的实现 ...

  7. 如何设计散列表(哈希表)

    如何设计散列表(哈希表) 可以获取到什么 通过本章可以了解散列表是什么数据结构,为什么叫做散列表?他的特点是什么?以及如何去设计一个散列表?为什么要这么设计? 会介绍散列表中三个重要的核心点:散列函数 ...

  8. 《算法导论》学习分享——11. 散列表(哈希表)

    11. 散列表(哈希表) 文章目录 11. 散列表(哈希表) 直接寻址表 散列表 链接法散列分析 散列函数 除法散列 乘法散列 全域散列 开放寻址法 线性探查 双重探查 开放寻址法分析 完全散列 涉及 ...

  9. 程序员的进阶课-架构师之路(16)-散列表(哈希表)

    前言 当我们在编程过程中,往往需要对线性表进行查找操作.在顺序表中查找时,需要从表头开始,依次遍历比较a[i]与key的值是否相等,直到相等才返回索引i:在有序表中查找时,我们经常使用的是二分查找,通 ...

最新文章

  1. 从消息处理角度看应用程序与windows的关系(图示)
  2. MySQL单行函数分类
  3. 另一种无法enable ABAP source code tool的原因
  4. 我们学的技术会过时吗?甚至被淘汰?
  5. 如何使用Elixir和Phoenix快速入门构建CRUD REST API
  6. 你必须懂的 T4 模板:深入浅出
  7. android 悬浮窗 输入框_利用DecorView实现悬浮窗的效果
  8. SpringBoot搭建天气预报微服务系统
  9. 【C/C++】字符串类型
  10. Python学习路程-常用设计模式学习
  11. gitlab创建分支上传文件_环境搭建:gitLab平台的搭建和简单使用
  12. Matlab遗传算法工具箱及应用
  13. 数据库(左、中、右)连接
  14. 用油猴子定制你的个性化插件
  15. 防止机械/移动硬盘休眠 - NoSleepHD
  16. 用C语言编写5个学生,每个学生有3门课程成绩,从键盘输入学生数据(学号,姓名,3门成绩)计算平均成绩,将成绩放入磁盘文件stud中
  17. MapboxGL设置地图背景透明
  18. Linux常用指令(详解)
  19. a.astype用法
  20. UE4官方的快捷键大全

热门文章

  1. [Linux] shell
  2. php中区分大小写的超全局变量总结
  3. 网络工程师求职与职业规划
  4. python学习笔记:(三)list(列表)常用的内置方法
  5. 一起学设计模式 - 命令模式
  6. Linux 编程中的API函数和系统调用的关系【转】
  7. Django基础之Model创建表
  8. spring mvc ajaxform IE下提示下载问题解决
  9. windows扩展C盘空间
  10. Java根据当前日期获得这一周的日期