哈希表总结

子曰:温故而知新,不温故前面学的就全tnnd的忘了。

哈希表的介绍

  1. 哈希表用来快速判断一个元素是否出现集合里。

例如要查询一个名字是否在这所学校里。

要枚举的话时间复杂度是 O ( n ) O(n) O(n),但如果使用哈希表的话, 只需要 O ( 1 ) O(1) O(1)就可以做到。

我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。

将学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数

  1. 如果学生的数量大于哈希表的大小怎么办,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表 同一个索引下标的位置。这时候就要用到哈希碰撞。
  2. 下图为哈希碰撞,和他的两种解决方法在这里插入图片描述
    拉链法—刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了
    在这里插入图片描述
    使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
    例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。
    在这里插入图片描述

用数组作为哈希表

在第一道题:有效的字母异位词
  1. 因为是26个字母,并且只有小写字母。所以我们首选数组。
  2. 让各个字符去减‘a’,得到数组下标位置,然后每出现1次,数组值就进行加1。
  3. 另外一个字符,同样的方法,不过是每出现一次就减1.
  4. 最终对数组每个元素进行判断只要不是0直接返回false。
在第六道题:赎金信

同样的方法,唯一区别就是最后的判断。

这种方式的缺点

数组是定长
数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。
如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

用set作为哈希表

适用情况

不需要对数据进行排序,而且还不要让数据重复(其实就是set的特点)

在第二道题:两个数组的交集
  1. 先把第一个数组加进set,然后里面就是不重复的数字。
  2. 在加第二个数组放进set的时候,用contains方法判断是否和第一个set有一样的元素,再放进set。
  3. 用result数组装下第二个set然后返回result
		int[] result = new int[set2.size()];
        int index = 0;
        for (int i : set2) {
            result[index++] = i;
        }
        return result;
在第三道题:快乐数

我们使用set来判断一个数字是否出现过(因为在快乐数中,出现一样的数字就代表进入了循环,return false)

  1. 对取数值各个位上的单数操作熟悉(先模10取余得到个位数。。。再除10整体数字右移)
  2. 判断这个n是否重复出现,如果重复了就是return false。set.contains(n)
  3. 否则加到set里面,再得到n的下一个值,一直找到n为1为止。
  public int getNumber(int n){
        int result = 0;
        while(n>0){
            int temp = n%10;
            result+= temp*temp;
            n = n/10;
        }
        return result;
    }

map作为哈希表

来说一说:使用数组和set来做哈希法的局限。

  1. 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
  2. set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
    由此我们可以看出map的适用范围
在第三道题:两数之和

在这里插入图片描述

for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]),i};
            }
            map.put(nums[i],i);
        }
        return null;
在第五道题:两数之和

那么就想到用数学中相反数的方式。
用HashMap先存入两个数组相加后的值存入到key中,其中可能会有重复的key,value就用于记录各个key对应的次数。显然两组是时间复杂度最低的。O(n的平方)
再用后两组计算其中的值,找到有-(和)=之前map存入的值,而输出的个数由各个value相加来决定
eg: 前两组key=2,value=3 找到后两组的和为-2,-2的相反数就是2,这时候result+=2。

双指针法

三数和四数之和

这两题是一个数组(集合)里找到和为0的组合,需要考虑到重复的问题,显然双指针法是最好的(其实更像是三指针和四指针)
比如三数之和
在这里插入图片描述

参考:https://programmercarl.com/%E5%93%88%E5%B8%8C%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E5%93%88%E5%B8%8C%E8%A1%A8

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值