查找 之 散列表查找(哈希表)

基础概念


散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系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);
    else
    printf("查找 %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.散列表的装填因子

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

    时间: 2024-08-10 21:29:28

    查找 之 散列表查找(哈希表)的相关文章

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

    散列表(哈希表) 比较难理解的官方定义:散列表/哈希表(Hash table),是根据关键码值(Key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. 举个例子,我们在查找中文字典时.假设我们要查找"翁weng",我们根据weng找到了对应的页码233,这个过程就是根据关键码值映射得到了表中的位置.然后我们在字典这个散列表中,根据我们刚才得到的位置 233页,直接访问了"

    漫画 | 什么是散列表(哈希表)?

    创建与输入数组相等长度的新数组,作为直接寻址表.两数之和的期望是Target,将Target依次减输入数组的元素,得到的值和直接寻址表比较,如果寻址表存在这个值则返回:如果不存在这个值则将输入数组中的元素插入寻址表,再进行输入数组中的下一个元素. 再进一步优化可以将输入数组直接作为直接寻址表,控制对应的下标就好,代码如下: Code:直接寻址表 class Solution { public int[] twoSum(int[] nums, int target) { for (int i =

    十三、散列表(哈希表)

    散列表 散列表插入分两步: 1. 根据散列函数找到索引 2. 处理索引冲突情况:拉链法和线性探测法 散列表是时间上和空间上作出权衡的一个例子.散列表采用函数映射找索引,查找很快,但是键的顺序信息不会保存(HashSet HashMap的本质) 散列函数 对于每种类型的键我们都学要一个与之对应的散列函数 正整数散列: 常用取余散列:k%M 浮点数散列: 例如0-1之间可以乘以一个M得到0-M-1之前的索引值,但是高位影响比低位大(0.12的1比2的影响更大,不符合均匀性),所以可以将键表示为二进制

    散列表(哈希表)

    1.按先后顺序存储在A[i]中,查找需要O(n),如果用二分查找,需要O(logn) 2.定义一个一维数组A[1..1353],使得A[key]=key,这样,查找只需O(1)就可以了,但空间开销比较大 思考:有什么办法使得查找时间快,占用空间小 哈希表基本原理 哈希表的基本原理是使用一个下标范围比较大的数组A来存储元素,设计一个函数h,对于要存储的线性表的每个元素node,取一个关键字key,算出一个函数值h(key),把h(key)作为数组下标,用A[h(key)]这个数组单元来存储node

    散列表(哈希表)的实现

    散列函数直接用key%size的形式,size为散列表的大小. 冲突处理采用平方探测法,为保证可以探测到整个散列表空间,散列表大小设置为4k+3形式的素数. 当散列表中的元素过多时会造成性能下降,这时应该倍增散列表的大小,重新计算原来散列表中每个元素在新的散列表中的位置. 散列表的实现 <span style="font-size:18px;">// HashTable.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h"

    大话数据结构—散列表查找(哈希表)

    一.基本概念 散列技术:在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key). f:散列函数/哈希函数: 采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表. 关键字对应的记录存储位置称为散列地址. 散列技术既是一种存储方法,也是一种查找方法. 散列技术适合求解问题是查找与给定值相等的记录.查找速度快. 散列技术不适合范围查找,不适合查找同样关键字的记录,不适合获取记录的排序,最值. 冲突:关键字key1不等于k

    数据结构复习之散列表查找(哈希表)

    一.散列表相关概念 散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key).公式如下: 存储位置 = f(关键字) 这里把这种对应关系f称为散列函数,又称为哈希(Hash)函数.按这个思想,采用散列技术将记录存在在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表.那么,关键字对应的记录存储位置称为散列地址. 散列技术既是一种存储方法也是一种查找方法.散列技术的记录之间不存在什么逻辑关系,它只与关键字有关,因此,散列主要是面向查

    查找之散列查找(哈希表)

    本学习笔记部分内容来自网易云课堂浙江大学数据结构课程,谢谢! 1.散列表(哈希表) 已知的几种查找方法: 顺序查找  O(N) 二分查找(静态查找)  O(logN) 二叉搜索树      O(h)  h为二叉树高度   (动态查找:有插入有删除有查找) 平衡二叉树      O(logN) 查找的本质:已知对象找位置 1.有序安排对象:全序或半序: 2.直接算出对象位置:散列. 散列查找法的两项基本工作: 1.计算位置:构造散列函数确定关键词存储位置: 2.解决冲突:应用某种策略解决多个关键词

    数据结构之散列表查找

    数据结构之--散列表查找 定义:通过某个函数f,使得 ?    ?    ?存储位置=f(关键字) ?    ?    ?这样我们可以通过查找关键字不需要比较久可以获得需要记录的存储位置.这就是一种新的存储技术--散列技术. ?    ?    ?散列技术在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key).查找时根据这个确定的对应关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上. ?    ?