哈希表详解---深入浅出

哈希表的产生就是为了查找而诞生的,是查找的时间极大的减少。

对于有海量数据要去搜索应该选用哈希表,虽然搜素树的速度也很快,但是哈希表稳定发挥算法复杂度可以达到o(1).

哈希表的缺点:表越满,性能越差,所以是典型的空间换时间。

一.朴素算法

首先我们先来讲一下朴素查找,比如查找值key为3,我们就要通过遍历整个数组来寻找数组是否有我们的Key,假设一共有N个元素,那么它的时间复杂度为O(n)。

 

如果遍历到了Key值则返回1,如果没有查找到则返回0.

以上就是朴素算法的查找思路。

二.哈希算法

1.Hash table的定义

哈希表就是根据Key关键值,作为自变量通过H(key)哈希函数对应到数组地址,这个数组就叫做散列表(Hash table)。

 2.Hash table的特例

key的关键值的范围为0—99,那我们就可以创建一个table[100]数组,哈希函数H(x) = x  。

a数组为Key数组,n为a数组的个数。

void create_hash(int a[],int n,int table[])
{
    int i;  //使用i遍历数组a中的全部n个元素,数组a为key数组
    for(i = 0;i<n;i++)
    {
        table[a[i]]++  //用Table的下标记录a[i]出现的次数

    }


}

 打印哈希表结果

查找哈希表中是否存在Key值

int find_key(int table[],int key)
{
    return table[key]!=0;

    //如果table[key]不是0返回1,否则返回0;
}

 

 

此时查找Key是否存在的时间复杂度为O(1);

看到这里小伙伴们是不是发现了就好似桶排序啊,其实桶排序的思想就是特殊的Hash Table,这种排序的算法复杂度为O(n),优于一般的log(n);

接下来我们思考一下,假如数组a中的数据范围不是0—99而是2的31次方,更或者不是int类型,而是浮点数,字符串,甚至是数组,对象等等更复杂的元素我们该怎么去处理呢?

三.哈希函数

为了解决上面的问题,我们就要引入哈希函数了。我们将带存储的数据转化为哈希表长范围内的整数,然后再使用数组下标进行访问。

1.整数数据,可以直接取余哈希表长,得到对应的哈希值

int int_func(int key)
{
    return key%MAX_table_len;

}

2.字符串,可以遍历字符串,把当中的字符他们的ASCII码值加起来,再取余表长得到哈希值

int string_func(const char *key)
{
    int sum = 0;
    while(*key)   //遍历字符串中的字符
    {
        sum += *key;  //将它们的ASCII码相加得到整数      
        key++;

    }
    
    return sum % MAX_table_len;  //转化为整数后再取余表长
}

例如:abc的ACSII码相加为94,则取余表长为94,存入table下标为94,则代表了

这时候问题又来了,abc和cba会映射到table[94],3和103都会映射到table[3],等等  这时候就造成了哈希冲突,冲突时就会导致查询出现问题。

 

四.哈希冲突

  首先呢我们在构造哈希函数时应遵循,哈希函数数值必须在散列地址的范围内分布均匀,尽量减少地址冲突。

接下来我们介绍两种处理哈希冲突的方式

1.开放定址法

开放地址法的核心就是方发生冲突时再次进行散列,寻找下一个地址。在寻找下一个地址时我们再次构造一个哈希函数,称为双哈希法。

设置第二个哈希的函数,例如:hash2(key) = R-(key mod R)

R要取比数组尺寸小的质数。

例如   R=7: hash(关键字) = 7-(关键字%7)

也就是说,二次哈希的结果在1-7之间,不会等于0;

如果遇到冲突,新位置 = 原始位置 + i*hash2(关键字)

例如:

现在哈希表的数组大小为13,第一个关键字为15,15 mod 13 == 2 , 所以存储在哈希表数组下标为2的地方,第二个关键字为2, 2 mod 13 == 2 , 所以也应该存储在哈希表数组下标为2的地方,但是此时发生了冲突,所以应该进行二次哈希处理。

设第二次哈希函数的R为7,那么

7 - (2  mod 7)== 5         新位置 = 原始位置(2)  +  i * hash2( 5 ) = 7;     (i=1)

若7此时也有存储则i=2,依次类推。

同理,大家可以自己尝试18和28,对应存储位置为5,9。

2.链地址法

把哈希表的数值全部变为指针数组,每一个指针指向一个单链表,这样一个数字就可以存储多个相同哈希值的Key。

以上呢我们解决了哈希冲突,可是新的问题又来了,在第一种情况下如果哈希表满了该怎么办?

经过重新计算后

 以上就是对哈希表概念的讲解,代码实现部分请看Up的哈希表C语言实现大全

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值