数据结构(查找-散列表(哈希表)的查找)

1. 散列表的基本概念

元素的存储位置和其关键字之间建立某种直接关系,这就是散列查找法。

(1) 散列函数和散列地址:在记录的存储位置p和其关键字key之间建立一个确定的对应关系H,使p=H(key),称这对应关系H为散列函数,p为散列地址。
(2) 散列表:一个有限连续的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表是一个一维数组,散列地址是数组的下标。
(3) 冲突和同义词:对不同的关键字可能得带统一散列地址,即key1 != key2,而H(key1) = H(key2),这种现象称为冲突。具有相同函数值得关键字对该散列函数来说称作同义词,key1 和key2互称为同义词。

2. 散列函数的构造方法

选取散列函数的参考:

  1. 计算散列地址所需的时间;
  2. 关键字长度;
  3. 散列表大小;
  4. 关键字的分布情况;
  5. 查找记录的频率。

(1) 直接定址法

其实就是直接通过取关键的字的某个线性值作为散列地址:f(key)=a*key+b,(a,b为常数)

(2) 数字分析法

假设某公司的员工登记表以员工的手机号作为关键字。手机号一共11位。前3位是接入号,对应不同运营商 的子品牌;中间4位表示归属地;最后4位是用户号。不同手机号前7位相同的可能性很大,所以可以选择后4 位作为散列地址,或者对后4位反转(1234 -> 4321)、循环右移(1234 -> 4123)、循环左移等等之后作为散列地址。

数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布比较 均匀,就可以考虑这个方法。

(3) 平方取中法

假设关键字是1234,平方之后是1522756,再抽取中间3位227,用作散列地址。平方取中法比较适合于不 知道关键字的分布,而位数又不是很大的情况。

(4) 折叠法

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

比如关键字是9876543210,散列表表长是3位,将其分为四组,然后叠加求和:987 + 654 + 321 + 0 = 1962,取后3位962作为散列地址。

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

(5) 除留取余数法

f(key) = key mod p (p≤m),m为散列表长。
这种方法不仅可以对关键字直接取模,也可在折叠、平方取中
后再取模。根据经验,若散列表表长为m,通常p为小于或等于表长(最好接近m)的最小质数,可以更好的 减小冲突。

(6) 随机数法

f(key) = random(key),这里random是随机函数。
当关键字的长度不等时,采用这个方法构造散列函数是比较合适的。

3. 处理冲突的方法

(1) 开放地址法

开放地址就是一旦发生冲突,就去寻找下一个空的散列地址,只有散列表足够大,空的散列地址总能找到,并且记录它。
至于如何寻找下一个空的散列地址,有三种方法

1. 线性探测法

f(key)=(f(key)+d)%m ,其中d取(0,1,2,3,4.....,m-1),m为散列表的长度


如上图所示,散列表的长度为12,而且我们现在已经插入了部分数据了,下面我们继续插入37,。然后,我们使用散列函数计算37的散列地址:
f(37)=f(37)%12=1
但是我们发现1这个位置已经存放了25,那么我们就继续寻找下一个空的散列地址。
f(37)=(f(37)+1)%12=2
发现2这个地址没有内容,所以把37插入到这个位置,得如下图的结果:



线性探测来解决冲突问题,会造成冲突堆积。所谓的冲突堆积就是比如说刚才的37,它本来是属于下标1的元素,现在却占用了下标为2的空间,这会造成待会我们需要存放本来要放在下标为2的元素时,再次发生冲突,这个冲突会一直传播下去,造成查找和插入效率都大大减低。

2. 二次探测法

f(key)=(f(key)+d)%m, ,其中d取(0^2,1^2,-1^2,2^2,-2^2,3^2,-3^2,4^2,-4^2...,q^2,-q^2),q<=m/2,m为散列表的长度

其实,这个是对线性探测的一个优化,增加了平方可以不让关键字聚集在某一块区域。



例如,我们对刚才的那个散列表,插入一个元素:7,通过二次探测的散列函数计算得到的散列地址为:
f(7)=f(7)%12=7
但是,我发现下标为7的位置已经存放了元素:67,所以我需要寻找下一个存储地址:
f(7)=(f(7)+1^2)%12=8
突然发现下标为8的地址也存放了56这个元素,所以我们只能继续往下寻找下一个存储地址:
f(7)=(f(7)+(-1^2))%12=6
发现下标为6的这个地址空间还是空的,所以我就把7插入到这个位置,得到如下结果:


3. 随机探测法

f(key)=(f(key)+d)%m, d为随机数列,而m为表长
在实际程序中应预先用随机数发生器产生一个随机序列,将此序列作为依次探测的步长。这样就能使不同的关键字具有不同的探测次序,从而可以避 免或减少堆聚。

(2) 链地址法

所谓的链地址法,其实就是当发生冲突时,我还是把它存放在当前的位置,只是每个位置都是使用链表来存放同义词,这个思路和图的邻接表存储方式很相似。如下图所示:


4. 散列表的查找

开放地址散列表存储表示

#define m 20 //散列表的长度
typedef struct{
    KeyType key;
    InfoType otherinfo;
}HashTable;
  1. 给定待查找的关键字key,根据造表时设定的散列函数计算H0=H(key)。
  2. 若单元H0为空,则所查找元素不存在。
  3. 若单元H0中元素关键字为key,则查找成功。
  4. 否则重复下述解决冲突的过程:
  • 按处理冲突的方法,计算下一个散列地址Hi。
  • 若单元Hi为空,则所查找元素不存在。
  • 若单元Hi中元素的关键字为key,则查找成功。
#define NULLKEY 0
int SearchHash(HashTable HT,KeyType key)
{
    H0 = H(key);//根据散列函数H(key)计算散列地址
    if(HT[H0].key == NULLKEY) return -1;
    else if(HT[H0].key == key ) return H0;
    else
    {
        for(i=1;i<m;i++)
        {
              Hi = (H0+i)%m;//按照线性探测法计算下一个散列地址Hi
              if(HT[Hi].key == NULLKEY) return -1;
              else if(HT[Hi].key == key ) return Hi;
        }
        return -1;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,846评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,146评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,789评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,656评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,467评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,870评论 3 389
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,500评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,790评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,828评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,628评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,449评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,881评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,077评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,376评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,879评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,089评论 2 338