代码随想录 Day14 哈希表 | LC454 四数相加II & LC383 赎金信

文章讲述了如何利用哈希法解决四数相加问题,通过统计四个数组中元素的组合并检查是否存在使得和为零的情况。同时介绍了如何使用哈希数组判断一个字符串能否由另一个字符串中的字符构成,且字母不可重复使用。

六、四数相加II

题目:

力扣454:四数相加II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:

  1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1

提示:

  • n == nums1.length
  • n == nums2.length
  • n == nums3.length
  • n == nums4.length
  • 1 <= n <= 200
  • -228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228

解题思路分析:


这道题不需要考虑去重的操作;

假如4个数组中的数值都是0,那么每个0都在不同的位置上,每种组合都是一个四元组,也就是说如果有重复数值的话,不同位置还是算作不同的元组,因此不需要去重(去重的话返回的四元组个数就是1)

如何想到哈希法?

类似于有效字母异位词,在一个集合中需要判断一个元素是否出现过;

思路:

假设有四个数组A、B、C、D;

  • 遍历数组A、B,分别取出两个元素a和b,将a+b放入哈希表中;

  • 遍历数组C、D,分别取出两个元素c和d,得到c+d;

  • 遍历数组C、D的时候,每得到一个c+d,就去哈希表中找有没有对应的a+b,让他们加起来等于0;

    • 这里相当于找0-(c+d),有没有在哈希表中
  • 如果有,就找到了一个匹配项,进行计数(count要加上匹配项的出现次数);

用数组下标做映射,值太大了不合适;所以考虑set和map;

在这道题中,不仅要统计a+b在哈希表中有没有出现过,还需要统计出现过多少次,所以用map;

  这里之所以要统计a+b出现的次数,是因为a+b到相同的值是不需要进行去重的;

  假设有如下所示的数组A和数组B:

在这里插入图片描述

  A[0] + B[0]:2+(-1)= 1 这是一个值,将1存入map中,出现次数记为1;

  A[5] + B[5]:5+(-4)= 1 和上面两个元素和相等,但是算作新的一个元组,那么就将map中1对应的出现次数+1;

代码讲解:

//定义一个变量count,用来记录最终的四元组个数
int count = 0;
//定义一个map,存放a+b的值以及值出现的次数
Map<Integer, Integer> map = new HashMap<>();
//遍历数组A和B,统计两个a+b的值以及值出现的次数,放入map
for(int i = 0; i < n; i++) {
  for(int j = 0; j < n; j++) {
    int sum = nums1[i] + nums2[j];
    //这句代码的含义是:如果 sum 已经存在于 Map 中,则取出对应的值(即已经存入的次数),然后将其加1,再放回到 Map 中,以实现记录 sum 出现的次数;如果 sum 不存在于 Map 中,则 map.getOrDefault(sum, 0) 返回默认值0,再加1,然后以键值对 (sum, 1) 的形式存入 Map 中,表示 sum 出现了1次。
    map.put(sum, map.getOrDefault(sum, 0) + 1);
  }
}
//遍历数组C和D,得到c+d,然后在map中找对应的a+b,同时记录匹配到的次数
for(int i = 0; i < n; i++) {
  for(int j = 0; j < n; j++) {
    int sum = nums3[i] + nums4[j];
    //在map中查找有没有对应的a+b=0-(c+d),有则返回次数,无则返回默认值0
    int record = map.getOrDefault(0 - sum, 0);
    //计数
    count = count + record;
  } 
return count;

复杂度分析:

  • 时间复杂度:O(n2),比暴力解法的4层for循环n4好很多
  • 空间复杂度:O(n2),最坏的情况下,a+b的值各不相同,个数为n2

题解:


HashMap:

在这里插入图片描述

七、赎金信

题目:

力扣383:赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例1:

输入:ransomNote = “a”, magazine = “b”
输出:false

示例2:

输入:ransomNote = “aa”, magazine = “ab”
输出:false

示例3:

输入:ransomNote = “aa”, magazine = “aab”
输出:true

提示:

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNote 和 magazine 由小写英文字母组成

解题思路分析:


说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。(意味着字母不可重复使用)

从题目中可以分析出:要判断前一个字符串中的字母是否在后一个字符串中出现过;

也就是要判断一个元素是否出现在集合里,可以用哈希法

考虑用哪种结构:由于小写字母只有26个,映射到数组中是范围有限的,可以考虑用数组

思路:(和有效字母异位词非常像)

可以用数组来记录第二个字符串中元素出现的次数,做加法;

然后遍历第一个字符串,出现的元素在数组中对应位置做减法;

如果最后数组中的元素都是大于等于0,那就代表字符串1可以由字符串2中的字母构成,返回true;

如果最后数组中的元素有小于0的,则表示不能构成,返回false。

题解:


哈希数组:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值