本次笔记内容:
11.3.1 开放定址法
11.3.2 线性探测
11.3.3 线性探测-字符串的例子
11.3.4 平方探测法
11.3.5 平方探测的实现
11.3.6 分离链接法

文章目录

  • 冲突处理方法常见思路
  • 散列表查找性能分析
  • 开放地址法(Open Addressing)
    • 线性探测法(Linear Probing)
    • 线性函数:字符串例子
    • 平方探测法(Quadratic Probing)
    • 平方探测法的实现
    • 双散列探测法(Double Hashing)
    • 再散列(Rehashing)
  • 分离链接法(Separate Chaining)

冲突处理方法常见思路

  • 换个位置:开放地址法;
  • 同一位置的冲突对象组织在一起:链地址法。

散列表查找性能分析

  • 成功平均查找长度(ASLs)
  • 不成功平均查找长度(ASLu)

比如:

如上图,ASLs即找每个元素的平均查找次数,即数字11需要查找1次(1+0次冲突次数);数字30需要查找7次(1+6次冲突次数)。

ASLu即,对于不存在的元素,如22、33(其余数为0),需要查找3次(3次后移动到空位上),才能确认不存在。对每一类余数加和求平均。

开放地址法(Open Addressing)

一旦产生了冲突(改地址已有其他元素),就按某种规则取寻找另一空地址。

若发生了第i次冲突,试探的下一个地址将增加did_idi​,基本公式是hi(key)=(h(key)+di)modTableSize(1≤i<TableSize)h_i(key)=(h(key)+d_i) \; mod \; TableSize (1 \le i < TableSize)hi​(key)=(h(key)+di​)modTableSize(1≤i<TableSize)

其中,did_idi​决定了不同的解决冲突方案:线性探测(di=id_i=idi​=i)、平方探测(di±i2d_i \pm i^2di​±i2)、双散列(di=i×h2(key)d_i=i \times h_2(key)di​=i×h2​(key))。

线性探测法(Linear Probing)

线性探测法:以增量序列1,2,…,(TableSize-1)循环试探下一个存储地址。

示例如下。

题目如上图。

如上图,线性探测会形成“聚集现象”。即数都聚在一起了。

线性函数:字符串例子

例子如上图,很简单。

平方探测法(Quadratic Probing)

平方探测法:以增量序列12,−12,22,−22,...,q2,−q21^2,-1^2,2^2,-2^2,...,q^2,-q^212,−12,22,−22,...,q2,−q2且q≤⌊TableSize/2⌋q \le \lfloor TableSize / 2 \rfloorq≤⌊TableSize/2⌋循环试探下一个存储地址。

此例子与线性探测所举相同。

如上图其ASLs在此例中好于线性探测2.56。

但是二次探测存在一个问题,即:在有空间的情况下,也不一定能找到空间。

如上图,上面展示了一个通过二次探测却无法找到空间的例子。

但是,有定理:如果散列表长度TableSize是某个4k+3(k是正整数)形式的素数时,平方探测法就可以探查到整个散列表空间。

平方探测法的实现

typedef struct HashTbl *HashTable;
struct HashTbl
{int TableSize;Cell *TheCells;
} H;HashTable InitializeTable(int TableSize)
{HashTable H;int i;if (TableSize < MinTableSize){Error("散列表太小");return NULL;}// 分配散列表H = (HashTable)malloc(sizeof(struct HashTbl));if (H == NULL)FatalError("空间溢出!!!");H->TableSize = NextPrime(TableSize);    // 找一个素数// 分配散列表 CellsH->TheCells = (Cell *)malloc(sizeof(Cell) * H->TableSize);if (H->TheCells == NULL)FatalError("空间溢出!!!");for (i = 0; i < H->TableSize; i++)H->TheCells[i].info = Empty;return H;
}

如上图,之所以留一个info变量,是因为考虑到删除操作时如果直接将元素置为空,则会产生问题,因此info可以表示“Deleted”已删除。

Position Find(ElementType Key, HashTable H)
{// 平方探测Position CurrentPos, NewPos;int CNum;CNum = 0;NewPos = CurrentPos = Hash(Key, H->TableSize);while (H->TheCells[NewPos].info != Empty &&H->TheCells[NewPos].Element != Key)){// 字符串类型的关键词需要strcmp函数if (++cNum % 2){// 判断奇偶NewPos = CurrentPos + (CNum + 1) / 2 * (CNum + 1) / 2;while (NewPos >= H->TableSize)NewPos -= H->TableSize;}else{NewPos = CurrentPos - CNum / 2 * CNum / 2;while (NewPos < 0)NewPos += H->TableSizee;}}return NewPos;
}

如上为查找新位置操作。判定Cnum奇偶是为了在did_idi​与Cnum间建立起映射。

映射关系如上图所示。

void Insert(ElementType Key, Hashtable H)
{Position Pos;Pos = Find(Key, H);if (H->TheCells[Pos].info != Lengitimate){// 确认在此插入H->TheCells[Pos].info = Legitimate;H->TheCells[Pos].Element = Key;// 字符串类型的关键词需要strcpy函数}
}

插入操作如上。在开放地址散列表中,删除操作要很小心。通常只能 “懒惰删除” ,即需要增加一个 “删除标记(Deleted)” ,而并不是真正删除它。以便查找时不会 “断链” 。其空间可以在下次插入时 重用

双散列探测法(Double Hashing)

偏移量也通过hash映射出来。

did_idi​为i∗h2(key)i*h_2(key)i∗h2​(key),h2(key)h_2(key)h2​(key)是另一个散列函数。探测序列成:h2(key),2h2(key),3h2(key),..h_2(key),2h_2(key),3h_2(key),..h2​(key),2h2​(key),3h2​(key),..

  • 对任意的key,h2(key)≠0h_2(key) \ne 0h2​(key)​=0;
  • 探测序列还应该保证所有的散列存储单元都应该能够被探测到。选择以下形式有良好的效果:h2(key)=p−(keymodp)h_2(key)=p-(key \; mod \; p)h2​(key)=p−(keymodp)

其中:p<TableSizep < TableSizep<TableSize,p、TableSize都是素数。

再散列(Rehashing)

  • 当散列表元素太多(即装填因子α太大)时,查找效率会下降;
  • 可以考虑将散列表扩大,如从11变成23;
  • 当扩大后,需要重新插入元素;
  • 实用最大装填因子一般取0.5≤α≤0.850.5 \le \alpha \le 0.850.5≤α≤0.85;
  • 当装填因子过大时,解决的方法是加倍扩大散列表,这个过程叫做“再散列(Rehashing)”。

分离链接法(Separate Chaining)

将相应位置上冲突的所有关键词存储在同一个单链表中

链地址法如上图。

typedef struct ListNode *Position, *List;
struct ListNode
{ElementType Element;Position Next;
};typedef struct HashTbl *HashTable;
struct HashTbl
{int TableSize;List TheLists;
};Position Find(ElementType Key, HashTable H)
{Position P;int Pos;Pos = Hash(Key, H->TableSize); // 初始散列表位置P = H->TheLists[Pos].Next;     // 获得链表头while (P != NULL && strcmp(P->Element, Key))P = P->Next;return P;
}

【数据结构笔记40】哈希表冲突处理方法:开放地址法(线性探测、平方探测、双散列、再散列),分离链接法相关推荐

  1. 学习数据结构笔记(8) ---[哈希表(Hash table)]

    B站学习传送门–>尚硅谷Java数据结构与java算法(Java数据结构与算法) 一般在java程序访问数据库时都会安排从内存的缓存层中取数据;之前的做法是自己写个哈希表,实现对数据的缓存. 哈 ...

  2. Python与数据结构[4] - 散列表[1] - 分离链接法的 Python 实现

    分离链接法 / Separate Chain Hashing 前面完成了一个基本散列表的实现,但是还存在一个问题,当散列表插入元素冲突时,散列表将返回异常,这一问题的解决方式之一为使用链表进行元素的存 ...

  3. j - 数据结构实验:哈希表_一看就懂的数据结构基础「哈希表」

    哈希表 哈希表(Hash table),是存储键值(Key Value)对数据的一种数据结构. 例如,我们可以将人的名字作为键,性别作为值来存储.通过把键映射到表中的一个位置来访问数据,以提高查找速度 ...

  4. JavaScript数据结构之 —— 08哈希表

    散列算法(也就是哈希)的作用是尽可能快地在数据结构中找到一个值.在之前如果要在数据结构中获得一个值(使用get方法),需要遍历整个数据结构来找到它. 所有元素根据和该元素对应的键,保存在数组的特定位置 ...

  5. 数据结构实验:哈希表

    数据结构实验:哈希表 题目描述 在n个数中,找出出现次数最多那个数字,并且输出出现的次数.如果有多个结果,输出数字最小的那一个. 输入 单组数据,第一行数字n(1<=n<=100000). ...

  6. 数据结构之哈希表的分离链接法java实现

    哈希表的分离链接法 原理 Hash Table可以看作是一种特殊的数组.他的原理基本上跟数组相同,给他一个数据,经过自己设置的哈希函数变换得到一个位置,并在这个位置当中放置该数据.哦对了,他还有个名字 ...

  7. 哈希表处理冲突的方法?

    哈希表处理冲突的方法? 一.基本概念 二.处理冲突的方法 (1) 链地址法 (2) 开放定址法 (3) 再散列法 (4) 建立一个公共溢出区 一.基本概念 哈希表,也叫散列表,是根据关键字而直接进行访 ...

  8. Java解决Hash(散列)冲突的四种方法--开放地址法(线性探测,二次探测,伪随机探测)、链地址法、再哈希、建立公共溢出区

    Java解决Hash(散列)冲突的四种方法--开放地址法(线性探测,二次探测,伪随机探测).链地址法.再哈希.建立公共溢出区 参考文章: (1)Java解决Hash(散列)冲突的四种方法--开放地址法 ...

  9. 冲突处理方法----开放定址法

    1 前言 常用处理冲突的思路: 换个位置: 开放地址法 同一位置的冲突对象组织在一起:链地址法 2 开放定址法(Open Addressing) 一旦产生了冲突(该地址已有其它元素),就按某种规则去寻 ...

最新文章

  1. nutz 结合QueryResult,Record 自定义分页查询,不构建pojo 整合
  2. Java创建线程的方式
  3. 细思极恐,你真的会写 Java 吗
  4. MYSQL中常用的强制性操作(例如强制索引)
  5. 首次公开!《阿里计算机视觉技术精选》揭秘前沿落地案例
  6. linux如何安装django
  7. Angular JS(二) 指令部分
  8. ActiveMQ - spring集成jms
  9. android 下载多个文件怎么打开,从android的下拉框中下载多个文件或文件夹?
  10. 函数进阶---闭包/装饰器/迭代器/生成器---高级特性
  11. asp.net 通过ajax方式调用webmethod方法使用自定义类传参及获取返回参数
  12. 从深圳到底特律——我的出国求学之路
  13. 百度文库复制内容,留作笔记
  14. android httpclient版本,Android studio使用http 没有 HttpClient
  15. JS 轮播图 图片切换(定时器)
  16. HashMap的应用
  17. Python中Django与Echarts的结合用法
  18. 《Fluent Python》学习笔记:第 8 章 对象引用、可变性和垃圾回收
  19. java代码实现15位身份证号码升级到18位
  20. 开启微信悬浮窗权限有什么用_用这一个App,干掉其他所有App的...所有广告

热门文章

  1. 攻防比赛_深度:一场攻防杂乱的比赛,凸显了索肖三中卫阵型的核心是谁
  2. 关于如何查看 EntityValidationErrors 详细信息的解决方法
  3. Jenkins持续发布解决方案
  4. IDEA使用@Data注解,类调用get、set方法标红的解决办法
  5. 关于System.AccessViolationException异常
  6. Java程序如何获得自己的进程ID?
  7. 如何在C#中获取Unix时间戳
  8. JAVA调用Web Service接口的五种方式
  9. activiti删除已经部署的流程定义
  10. php 读取mysql 二维数组_PHP操作 二维数组模拟mysql函数