尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

详细介绍:代码随想录第七天|哈希表part02--454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

详细介绍:代码随想录第七天|哈希表part02--454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和
📅 发布时间:2026/6/20 17:15:45

详细介绍:代码随想录第七天|哈希表part02--454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

2025-11-18 22:21  tlnshuju  阅读(0)  评论(0)    收藏  举报

资源引用:

leetcode题目:

454.四数相加Ⅱ(454. 四数相加 II - 力扣(LeetCode))

383.赎金信(383. 赎金信 - 力扣(LeetCode))

15.三数之和(15. 三数之和 - 力扣(LeetCode))

18.四数之和(18. 四数之和 - 力扣(LeetCode))

例行碎碎念:

今天也追赶上了一些进度,虽然生病感冒,但今天很好的坚持了从早到晚的复习,秉承开源的精神我也将自己的复习资料整理出来分享给其他同学,这也给了我一些成就感!在晚上虽然很疲惫,但还是坚持完成了一天的代码随想录,哈希表的利弊变得更加清晰,数组的小而美也令我认知刷新,更重要的是自己能够很好的复用先前学到的技巧(尤其是双指针),继续加油!

454.四数相加Ⅱ(454. 四数相加 II - 力扣(LeetCode))

题目分析:

四个长度为n的整数数组,分别从中取元素组成四项元组,问有多少元组的项之和为0,最后返回符合条件的元组数,这是讨论是否存在的问题,并返回存在数量的问题,需要存储key为元素值,value为元素个数,考虑使用HashMap。

解题思路:

四个数组的长度均为n,则元组最多有n^4个。边界条件是n为零,即四个数组均为空时,直接返回0。

关键在于是四个map,还是value是四个下标。

若采用四个下标的形式进行存储,则时间复杂度极高,与暴力解法没有差别,且key值不能重复,该解法无法实现。

考虑使用类似二分法的思想,将num1和num2作为一个组合,将num3和num4作为一个组合,每个组合分别具有n^2个元组,时间复杂度从n^4缩减到n^2,第一个组合用map1存储,key为num1和num2中元素和的不同情况,value为不同情况存在的组合数;第二个组合用map2存储同理。对map1中的每个key值,从map2中检查是否存在其相反数,若存在,则将二者的value值相乘并加到返回值res上。

class Solution {public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {if(nums1.length==0 || nums2.length==0 || nums3.length==0 || nums4.length==0){return 0;}Map map1 = new HashMap<>();Map map2 = new HashMap<>();int res=0;for(int i : nums1){for(int j : nums2){int sum1 = i+j;int new_value=0;if(map1.containsKey(sum1)){new_value = map1.get(sum1)+1;}else{new_value = 1;}map1.put(sum1,new_value);}}for(int k : nums3){for(int l : nums4){int sum2 = k+l;int new_value=0;if(map2.containsKey(sum2)){new_value = map2.get(sum2)+1;}else{new_value = 1;}map2.put(sum2,new_value);}}for (Map.Entry entry : map1.entrySet()) {int sum1 = entry.getKey();if(map2.containsKey(-sum1)){res+=(entry.getValue()*map2.get(-sum1));}}return res;}
}

进一步简化:

c和d遍历时无需放入哈希表中,为减少开销,若存在(c,d)组合的两数之和的相反数存在于map1中,直接取map1对应的value加到计数res上即可。

class Solution {public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {if(nums1.length==0 || nums2.length==0 || nums3.length==0 || nums4.length==0){return 0;}Map map1 = new HashMap<>();int res=0;for(int i : nums1){for(int j : nums2){int sum1 = i+j;int new_value=0;if(map1.containsKey(sum1)){new_value = map1.get(sum1)+1;}else{new_value = 1;}map1.put(sum1,new_value);}}for(int k : nums3){for(int l : nums4){int sum2 = k+l;if(map1.containsKey(-sum2)){res+=map1.get(-sum2);}}}return res;}
}

再进一步简化:

map.getOrDefault(sum, 0) 的使用:如果存在key为sum的值则返回其value,否则返回默认值(此处参数将其规定为0)

class Solution {public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {int res = 0;Map map = new HashMap();//统计两个数组中的元素之和,同时统计出现的次数,放入mapfor (int i : nums1) {for (int j : nums2) {int sum = i + j;map.put(sum, map.getOrDefault(sum, 0) + 1);}}//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数for (int i : nums3) {for (int j : nums4) {res += map.getOrDefault(0 - i - j, 0);}}return res;}
}

383.赎金信(383. 赎金信 - 力扣(LeetCode))

题目分析:

两个字符串,判断ransomNote中的字符是否全部包含在magazine中,且每个字符是否有其一一对应,即magazine中的每个字符只能在ransomNote,由于是存在问题且不仅需要匹配元素值还要匹配个数,考虑用HashMap解决。

解题思路:

map的键值对存储magazine中的元素值及其出现次数,遍历ransomNote中的每个字符,如果该字符在map中存在,若其value值不为0则将其对应的value值减一,若其value值为0则直接返回false;若该字符不存在,也直接返回false。最终返回true。

class Solution {public boolean canConstruct(String ransomNote, String magazine) {Map map = new HashMap<>();for(char i : magazine.toCharArray()){map.put(i,map.getOrDefault(i,0)+1);}for(char j : ransomNote.toCharArray()){if(map.getOrDefault(j,0) == 0){return false;}else{map.put(j,map.getOrDefault(j,0)-1);}}return true;}
}

反思总结:

灵活运用了for-each循环和map.getOrDefault()方法

然而,map的空间消耗和哈希函数的调用消耗导致:不论是时间层面还是空间层面,该题使用map都不如使用数组,使用数组更加简单有效!

也不要忘了控制边界条件!

class Solution {public boolean canConstruct(String ransomNote, String magazine) {// 边界条件if (ransomNote.length() > magazine.length()) {return false;}// 定义一个哈希映射数组int[] record = new int[26];// 遍历for(char c : magazine.toCharArray()){record[c - 'a'] += 1;}for(char c : ransomNote.toCharArray()){record[c - 'a'] -= 1;}// 如果数组中存在负数,说明ransomNote字符串中存在magazine中没有的字符for(int i : record){if(i < 0){return false;}}return true;}
}

15.三数之和(15. 三数之和 - 力扣(LeetCode))

题目分析:

由于这题要返回的是元组本身,故使用哈希表很不方便,carl哥提示用双指针法。

该题仅有一个数组nums,若长度小于3则直接返回空的List<List<Interger>> res。

关键在于怎样在遍历过程中保证i,j,k两两不相等,且返回元组不可重复。

妙解:

为了保证元组不可重复,需要在遍历过程中避免重复使用值相同的元素,所以将数组排序!

初始化:假设已经从小到大排序,下标i从0开始遍历,left=i+1,right=nums.length-1(1.注意针对i的“去重” )

寻址:若三数之和大于0,说明偏大,right左移1位;若三数之和小于0,说明偏小,left右移1位。重复“寻址”过程,直到left=right为止,在这过程中遇到三数和为0的添入List(全程注意去重!)。

关于去重:
1.i的去重:

由于已经经过排序,如果可以减少对nums[i]相同情况的遍历,但应当注意判断条件应为nums[i] != nums[i-1],而非nums[i] != nums[i+1],因为元组不能重复,但元组内的元素值可以重复!

2.三数之和为0时,left和right相遇前的去重:

参考i的去重,可知应为nums[left]!=nums[left-1],nums[right]!=nums[right+1]吗?注意处理下标防止越界——right+1存在越界风险,且由于left和right不是锚定数所以前后去重无本质区别,考虑方便实现即可。

总结反思:

1.双指针法一定要排序!

2.边界条件处理:①数组长度检查②排序后,nums[i]为正数时,则三数之和不可能为0,直接返回当前result。

class Solution {public List> threeSum(int[] nums) {List> result = new ArrayList<>();Arrays.sort(nums);for (int i = 0; i < nums.length; i++) {// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if (nums[i] > 0) {return result;}if (i > 0 && nums[i] == nums[i - 1]) {  // 去重acontinue;}int left = i + 1;int right = nums.length - 1;while (right > left) {int sum = nums[i] + nums[left] + nums[right];if (sum > 0) {right--;} else if (sum < 0) {left++;} else {result.add(Arrays.asList(nums[i], nums[left], nums[right]));// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;right--;left++;}}}return result;}
}

18.四数之和(18. 四数之和 - 力扣(LeetCode))

题目分析:

/*与454.四数相加Ⅱ不同之处在于:①在同一个数组中取数,由于下标不可重复,因此相较于对4个数组操作的难度增大②返回值是满足条件的四元组,而非元组数*/

在15.三数之和的基础上仍然使用双指针。

解题思路:

先将数组排序,a从0下标开始遍历,注意剪枝和去重

嵌套循环b从a+1开始遍历,注意剪枝和去重

初始化left=b+1,right=nums.length-1,

循环不变量是right>left。

b在nums.length-3处为最后一次,a在nums.lengh-4处为最后一次。

其他步骤参考15.三数之和。

tips:同样注意检查剪枝条件:①nums数组长度不小于4②此题target并非确定的值,需要进一步约束剪枝条件为nums[i] > target && (nums[i] >=0 || target >= 0)

总结反思:

1.双指针法就是将类似N数之和的问题的时间复杂度从O(n^N)降为O(n^(N-1))

2.注意数值大小,为防止溢出,需要使用长整型long记录sum

import java.util.*;
public class Solution {public List> fourSum(int[] nums, int target) {Arrays.sort(nums);  // 排序数组List> result = new ArrayList<>();  // 结果集for (int k = 0; k < nums.length-3; k++) {// 剪枝处理if (nums[k] > target && nums[k] >= 0) {break;}// 对nums[k]去重if (k > 0 && nums[k] == nums[k - 1]) { //注意当k为0时k-1越界,故做此处理continue;}for (int i = k + 1; i < nums.length-2; i++) {// 第二级剪枝if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {break;}// 对nums[i]去重if (i > k + 1 && nums[i] == nums[i - 1]) { //与k为0时的处理类似,但重点是i为k+1时不必去重continue;}int left = i + 1;int right = nums.length - 1;while (right > left) {long sum = (long) nums[k] + nums[i] + nums[left] + nums[right];if (sum > target) {right--;} else if (sum < target) {left++;} else {result.add(Arrays.asList(nums[k], nums[i], nums[left], nums[right]));// 对nums[left]和nums[right]去重while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;right--;left++;}}}}return result;}
}

相关新闻

  • 以太网交换机的吞吐量
  • 7.2.1-内核bpf的实现原理
  • noip9

最新新闻

  • 图片格式转换工具怎么选?看这6款小程序对比结果 - 软件工具教程方法
  • PaddleOCR完整指南:从图像到结构化数据的AI文档解析革命
  • 无保卡老旧腕表没人收?南京回收不设门槛,新旧都收 - 讯息早知道
  • Python计算机毕设之基于 Django 的校园二手交易撮合平台设计与实现 高校闲置资源共享交易管理系统的设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • GitLens配置系统深度解析:高性能分布式Git可视化架构设计与实现原理
  • 孩子近视预防技术全解析 从检测到管控的实操指南 - 起跑123

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号