数据结构-Hash(哈希)基本特征


1. Hash(哈希)基础

1.1 Hash的概念和基本特征

Hash(哈希)也称为散列,就是把任意长度的输入,通过散列算法,变成固定长度的输出,这个输出值就是散列值。
Hash的映射到底是什么?为什么访问的时间复杂度为O(1)?
这里我们举例回答:
我们现在假设数组array存放1-15这些数,现在要存到一个大小是7的Hash表中,该如何存?我们的存储位置计算公式为:
index = number % 7
这时将1-6存入:
在这里插入图片描述
上面就是简单的取模。然后继续存7-13,结果如下:
在这里插入图片描述
最后再存14,15
在这里插入图片描述
但是这里有一些数据被存到了同一个位置,后面我们会讨论。
假如我要测试访问13是否在结构中,用上面的存储位置计算公式,很明显 13 % 7 = 6,我们直接访问array[6]这个位置,很明显是在的,所以返回true
假如我要测试访问20是否在结构中,用上面的存储位置计算公式,很明显 20 % 7 = 6,我们直接访问array[6]这个位置,只有6和13,所以返回false
理解这个例子我们就理解了Hash是如何进行最基本的映射的,还有就是为什么访问的时间复杂度为O(1)


2. 碰撞处理方式

在上面例子中,我们会发现有些在Hash中很多位置可能要存两个甚至多个元素,单纯的数组无法满足,这种两个不同的输入值(比如上面的6和13),根据同一散列函数(比如上面的index = number % 7)计算出的散列值相同的现象叫做碰撞

常见的解决方法:

  1. 开放定址法(java里的Threadlocal,不需要提前掌握,后面会简要说明)
  2. 链地址法(java里的ConcurrentHashMap,同上)

2.1 开放定址法

开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大(前提是要足够大),空的散列地址总能找到并存入。
在这里插入图片描述
例如上面存放7,8,9时,7直接存入索引为0的位置。8本来应该存到索引为1的位置,但是已经满了,所以向后找,索引3的位置为null,所以8存到索引3的位置,同理9存到6的位置。
问题:
这样会不会造成混乱?比如再存入3和6时,会发现自己的位置被外来值占领了,如何处理?
这里学习Java里的ThreadLocal才揭开。这里说一下基本思想:ThreadLocal有一个专门存储元素的ThreadLocalMap,每次getset元素时,会先将目标位置前后的空间搜索一下,将标记为null的位置回收掉,这样大部分不用的位置就收回来了。


2.2 链地址法

将哈希表的每个单元作为链表的头节点,所有哈希地址为 i 的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头节点的链表的尾部。例如:
在这里插入图片描述
这种处理方法代价还是比较高的,很多地方需要优化,例如Java中的ConcurrentHashMap中就使用了这种方式,其中涉及元素尽量均匀、访问和操作速度要快、线程安全、扩容等很多问题。

建议读者先理解java基础中的HashMap底层原理再来阅读
来看看下面这个Hash结构,下图有两处非常明显的错误,请思考一下:
在这里插入图片描述
解读
首先是数组长度必须是2的n次幂,这里长度为9,明显有误,然后是entry的个数不能大于数组的长度,如果大于就会触发扩容机制进行扩容,这里明显是大于75%,正确图解:
在这里插入图片描述
数组的长度即是2的n次幂,而他的size又不大于数组长度的75%。HashMap的实现原理是先要找到要存放数组的下标,如果是null的就存进去,如果不是null的就先判断key值是否一样,如果一样就替换,如果不一样就以链表的形式存在链表中(从JDK8开始,根据元素数量选择使用链表还是红黑树存储。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值