Hash-散列表查找(哈希公式的设计与哈希冲突的解决方案)

散列技术

散列技术是记录的存储位置和它的关键字之间建⽴一个确定的对应关系f,使得每 个关键字key对应一个存储位置f(key). 查找时,根据这个对应关系找到给定值 key的映射f(key). 若查找集合中存在这个记录,则必定在f(key)的位置上.

构造散列函数
  • 直接定址法
  • 数字分析法
  • 平方取中法
  • 折叠法
  • 除留余数法
  • 随机数法

直接定址法:将关键字作为散列地址。 f(key) = a * key + b (a,b为常数);
**数字分析法:**对比较长的关键字进行截取,经常处理关键字位数较大的情况;

**平方取中法:**关键字平方后取中间若干位数字;

折叠法:就是将关键字从左到右分割成位数相等的几部分(注意最后一部分位数不够可以稍微短些); 然后将几部分叠加求和,并按散列表长,取后几位作为散列列地址.适合不知道关键字如何分布的,而且关键字位数很多。

除留余数法:f(key) =keymodp(p<=m)

随机数法: f(key) =random(key);

标准:

  • 计算散列表地址所需时间(哈希函数的复杂度)
  • 关键字的长度
  • 散列表的大小
  • 关键字的分布情况
  • 记录查找的效率
冲突解决方案
开放定址法

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

开放定址法公式1:

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

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

开放定址法公式2:

fi (key)=(f(key)+di )Modm ;
di = {12, -12,22,-22,…,q2,-q2, q<=m/2 }

解决冲突的开放定址法称为 二次探测法

再散列函数法

准备多个散列函数,在发生冲突时,选择不同的散列函数。

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

RHi 指的是不不同的散列列函数.

链地址法

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

公共溢出区法

分为基本表和溢出表。

散列表查找实现
散列表结构设计
//定义散列表长为数组的长度
#define HASHSIZE 12
#define NULLKEY -32768

typedef struct
{
    //数据元素存储基址,动态分配数组
    int *elem;
    //当前数据元素个数
    int count;
}HashTable;
int m=0; /* 散列表表长,全局变量 */
初始化散列表
//1.初始化散列表
Status InitHashTable(HashTable *H)
{
    int i;
    
    //① 设置H.count初始值; 并且开辟m个空间
    m=HASHSIZE;
    H->count=m;
    H->elem=(int *)malloc(m*sizeof(int));
    
    //② 为H.elem[i] 动态数组中的数据置空(-32768)
    for(i=0;i<m;i++)
        H->elem[i]=NULLKEY;
    
    return OK;
}
散列函数
//2. 散列函数
int Hash(int key)
{
    //除留余数法
    return key % m;
}
插入关键字进散列表
//3. 插入关键字进散列表
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;
        
        //④H.elem[*addr] 等于初始值或者循环有回到了原点.则表示关键字不存在;
        if (H.elem[*addr] == NULLKEY || *addr == Hash(key))
            //则说明关键字不存在
            return UNSUCCESS;
    }
    
    return SUCCESS;
}
一句话总结

冲突的必然的,发生冲突是偶然的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值