数据结构--哈希表查找

一、引言

  • 哈希表查找也是一种查找方式,在此之前,如果了解其它查找方法,然后对比该方法最好
  • 本篇文章主要从两个方面介绍哈希表,一是哈希表的构造方法,另一个是哈希表的处理冲突方法。
  • 同时,本篇文章介绍元素类型为数字整型。
  • 红色尾非常主要内容,蓝色为细节内容。

二、哈希表概念及原理

1.  哈希表定义:

哈希表(散列表), 是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列

2.  哈希表基本思想

  • 可能不同的关键字会映射到同一个散列地址上,可能不同的关键字会映射到同一个散列地址上,即h(key i) = h(key j)(当key i ≠key j,称为“冲突(Collision)”。----需要某种冲突解决策略
  • 甚至可以理解为:直接按照某个公式查找元素地址位置

  • 哈希表的时间复杂度几乎为常量O(1),即与问题规模无关。

三、哈希表实现

PS:哈希表部分有两个十分重要的内容,哈希表的构造方法,哈希表的处理冲突方法

1.  简介部分

  •  哈希表的构造方法

考虑因素:计算哈希函数的时间,关键字长度,哈希表大小,关键字分布情况,记录查找的频率(可从这几个方面来理解)

直接地址法,数字分析法,除留余数法,折叠法,平方取中法等...

  •  哈希表处理冲突方法

考虑因素:同一地址存在多个元素 ,产生冲突。

开放定址法,链地址法,再哈希法,公共溢出法

 2. 哈希表构造方法及其原理

  • 直接地址法:取关键词的某个线性函数值为散列地址

       H(key) = a x key +b ,相当于一次函数。例如:h(key)=key-1990

           备注:由该地址所得地址集合与关键字集合大小相同,故不会产生冲突。但是实际极少使用。

  • 除留余数法 h(key) = key mod p 例如:h(key) = key % 17通常来说,p取值为素数 

           备注:会产生冲突。很常用,但是p的取值要十分注意。

  • 数字分析法:分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址

         例如上图的身份证号码:选择标红的6位,即变化可能性最大的6位作为散列地址

         备注:适合处理关键字位数多的情况,事先知道关键字分布最好。

  • 折叠法把关键词分割成位数相同的几个部分,然后叠加

         例如:56793542.  542+793+056=1391哈希地址为391

         备注关键字位数很多,并且关键字每一位上的数字分布大致均匀可用

  • 平方取中法:取关键字平方后的中间几位为散列地址

        例如:56793542   56793542 x 56793542=322550641290564; 则取641为散列地址

        备注较为常用的方法。不需知道关键字的全部情况,平方后的中间几位与数每一位都相关,以此建立连接。

附加字符关键词散列函数构造:ASCII码加和法,前三个字符移位法,移位法。

3.  哈希表的处理冲突方法及其原理

PS: 大致分为两种:换个位置存储冲突元素(开放地址法),同一位置的冲突元素组织在一起(链地址法)

  • ·开放地址法:一旦产生冲突,就按照某种规则去寻找另一个空地址,增量序列不同

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

                       Ⅱ 二次探测:以增量序列 1^2, -1^2, 2^2, -2^2, … (TableSize/2)平方,- (TableSize/2)平方

                        随机探测法:增量序列由随机函数产生,随机增量。

                       Ⅳ 再散列函数法:当散列表元素太大时,(通常为50%-85%最合适),加倍扩大散列表即可。

PS:TableSize表示哈希表的大小

  • 再哈希法:Hi=R·Hi(key);即产生冲突时,计算另外一个哈希函数地址,直到不产生冲突。
  • 链地址法将相应位置冲突的suoy2关键词存储在同一个单链表中。(Redis数据库,STL模板库常用

           例如:设关键字序列为 { 47, 7, 29, 11, 16, 92, 22, 8, 3, 50, 37, 89, 94, 21 };

    散列函数取为:h(key) = key mod 11;

 

查找成功的平均查找次数: ASLs =9+5*2/ 14 ≈ 1.36

  • 公共溢出法:与再散列方法相似,为所有冲突的关键字建立一个公共溢出区存取地址

4.  利用除留余数法构造,开发地址发解决冲突的算法实现。

#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12  //定义哈希表长为数组的长度
#define NULLKEY -32768
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; //查找成功返回
}

 

四、哈希表运用场景

见下面链接:Redis中的Hash数据类型及其使用

http://www.cleey.com/blog/single/id/808.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值