算法笔记:散列表的实现一

散列表 就是哈希表。
思想是:用数组支持按照下标随机访问数据的特性实现的一种数据结构,时间复杂度是O(1)。是数组的一种扩展。
散列表中使用散列函数把元素的键值映射为下标,将数据存储在数组中对应的下标中。
查询元素的时候用同样的散列函数,将键值转化为数组下标,从而读取到位置。

散列函数

散列函数 是用来把Key进行散列的一个方法。
设计基本要求:

  • 计算的散列值是一个非负整数
  • key值相等,散列后的值也相等。
  • key值不相等,散列后的值不相等。
    著名的哈希函数算法 MD5,SHA,CRC算法也无法避免哈希冲突。所以第三点要求无法达到完美的匹配。

散列冲突

哈希冲突是 计算hash值,两个不同的key 得到两个一样的hash值。
常用的哈希冲突解决方案有两类:开放寻址法与链表法

开放寻址法

核心思想:出现散列冲突,重新探测一个空闲位置将其插入。

线性探测
  1. 插入数据
    往链表中插入数据,某个数据经过散列后存储位置被占用,从当前位置开始往后进行查找,查找到空闲位置就将数据插入到空闲位置。


    摘自极客时间
  2. 查找数据
    通过散列函数求出要查找元素的键值对对应的散列值,比较数组下标中的散列值是否相等于要查找的元素。相等,则是我们要查找的数据,不相等则按照顺讯往后依次查找。遍历到数组空闲的位置还没有查找到需要的元素,就说明要寻找的元素没有在散列表中


    摘自极客时间
  3. 删除数据
    删除数据,并不是真的把数据删除,而是在数据上标记为deleted,线性探查的时候遇到标记为deleted空间不停止,继续往下探测。
  4. 存在的问题
    线性探测,插入的数据越多,散列冲突的可能性越大,并且空闲位置越来越少,极端情况下可能需要将整个散列表进行遍历寻找空间。
二次探测和双重散列

二次探测在一次探测的基础上进行步长的平方。
双重散列使用的是一组散列函数,先用第一个散列函数计算哈希值,如果被占用,在用第二个散列函数,如果还被占用,以此类推进行第三个第四个散列函数,直到找到空闲的存储位置。

采取哪种方式的探测,随着数据量的增大,空闲位置不多的情况,哈希冲突的概率就会增大。因此就需要保证散列表中存储一定比例的空闲槽位。
解决方案:装载因子
装载因子 = 填入表中的元素个数/散列表的长度
装载因子越大,空闲位置越少,冲突越多,性能就会下降。

链表法

在散列表中 增加桶或者槽的概念,Java中就是类似的概念。并且每个槽对应一条链表,所有散列表值相同的元素,我们都放到相同槽位对应的链表中。


摘自极客时间

插入时间复杂度是O(1);
查找与删除,计算出hash值之后,去链表中进行遍历。
时间复杂度与链表的长度k有关,也是就是O(K)。
散列均匀的散列函数 k = n/m 。 n代表的是散列中数据的个数,m代表散列表中槽的个数。

问题

10万条URL访问日志,按照访问次数给URL排序?
两个字符串数据,每个数组大约有10万条字符串,快速查找数组中相同的字符串。

你可能感兴趣的:(算法笔记:散列表的实现一)