代码随想录算法训练营第七天| 哈希表理论基础 ,454.四数相加II, 383. 赎金信, 15. 三数之和, 18. 四数之和

454.四数相加II

建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。

题目链接/文章讲解/视频讲解:

看到题目的第一想法:

超时的想法:用map存储其中的一个数组,然后三层for循环求得三个整数数组的和sum,最后判断map中是否存在0-sum,如果存在result加上它的value值,如果不存在就下层循环。显然这种想法的时间复杂度为O(n^3).
重新思考后的想法:用map的key存储其中两个数组的所有和,value存储其个数,然后三层for循环求得另外两个数组的sum,最后判断map中是否存在0-sum,如果存在result加上它的value值,如果不存在就下层循环。这种想法的时间复杂度为O(n^2),减少了一个数量级。

package com.second.day7;import java.util.HashMap;public class FourSumCount_454 {public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {HashMap<Integer, Integer> map = new HashMap<>();for(int i = 0; i < nums3.length; i++) {int num3 = nums3[i];for(int j = 0; j < nums4.length; j++) {int num4 = nums4[j];map.put(num3 + num4, map.getOrDefault(num3 + num4, 0) + 1);}}int result = 0;for(int i = 0; i < nums1.length; i++) {int num1 = nums1[i];for(int j = 0; j < nums2.length; j++) {int target = 0 - num1 - nums2[j];if(map.containsKey(target)) {result += map.get(target);}}}return result;}
}

看完代码随想录之后的想法:

思路是一样的,难怪leetcode的执行时间还挺快的。

自己实现过程中遇到哪些困难:

无,看到时间超了,能很快想出另外一种想法不错的。

383. 赎金信

建议:本题 和 242.有效的字母异位词 是一个思路 ,算是拓展题

题目链接/文章讲解:

看到题目的第一想法:

1.设置一个map,map的key表示magazine的字符,value表示字符出现的次数。
2.遍历magazine,将magazine的字符存入map中
3.遍历ransomNote,如果map中有不存在的字符,或map的字符的value小于0就返回false,直到遍历完。

package com.second.day7;import java.util.HashMap;public class FourSumCount_454 {public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {HashMap<Integer, Integer> map = new HashMap<>();for(int i = 0; i < nums3.length; i++) {int num3 = nums3[i];for(int j = 0; j < nums4.length; j++) {int num4 = nums4[j];map.put(num3 + num4, map.getOrDefault(num3 + num4, 0) + 1);}}int result = 0;for(int i = 0; i < nums1.length; i++) {int num1 = nums1[i];for(int j = 0; j < nums2.length; j++) {int target = 0 - num1 - nums2[j];if(map.containsKey(target)) {result += map.get(target);}}}return result;}
}

看完代码随想录之后的想法:

思路是一样的,但卡哥用的是数组存储,比我直接用map的空间少,时间更快。以后涉及只有小写字母或者大写字母的,用哈希法先用数组存储。

自己实现过程中遇到哪些困难:

15. 三数之和

建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。

题目链接/文章讲解/视频讲解:

看到题目的第一想法:

1.设置一个map,map的key表示nums[i]的值,value表示出现的次数
2.用map存储其中的一个数组
3.两层for循环,分别求得nums[i]和nums[j],然后判断map中是否有0-nums[i]-nums[j]
4.如果map中存在,放入list中,为了去重,排序后放入set集合中

package com.second.day7;import java.util.*;public class ThreeSum_15 {/*** 一顿操作猛如虎,点击提交超时了。** 二话不说翻题解,评论区里全人才。** 反反复复终得道,再次尝试却报错。** 行行检查字字改,击败用户百分五。* @param nums* @return*/public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> result = new ArrayList<>();Set<List<Integer>> set = new HashSet<>();//key存储nums[i]的值,value存储值的个数HashMap<Integer, Integer> map = new HashMap<>();for(int num : nums) {map.put(num, map.getOrDefault(num, 0) + 1);}for(int i = 0; i < nums.length - 1; i++) {int num1 = nums[i];map.put(num1, map.get(num1) - 1);for(int j = i + 1; j < nums.length; j++) {int num2 = nums[j];map.put(num2, map.get(num2) - 1);int target = -num1 - num2;List<Integer> list = new ArrayList<>();if(map.containsKey(target) && map.get(target) > 0) {list.add(num1);list.add(num2);list.add(target);Collections.sort(list);set.add(list);}map.put(num2, map.get(num2) + 1);}map.put(num1, map.get(num1) + 1);}for(List<Integer> lists : set) {result.add(lists);}return result;}
}

看完代码随想录之后的想法:

排序后去重的效率是很低的,然后我的时间复杂度就很高,Colletion.sort()用的归并排序或者快速排序时间复杂度为O(nlogn),然后加两层for循环时间复杂度就到了O(n^3logn).
卡哥有去重的哈希法:先对数组进行排序,两层for循环,然后每层for循环分别去重nums[i],nums[j],在第一层for循环设置一个set用来存储nums[k],如果set中存在target = nums[i] - nums[j],则符合题目要求,放入result中,并对nums[k]去重。剪枝后的时间复杂度是O(n^2)。

public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(nums);for(int i = 0; i < nums.length; i++) {//排序后的第一数都大于0,之后的肯定不满足三数之和为0if(nums[i] > 0)break;//对a去重if(i > 0 && nums[i] == nums[i - 1])continue;HashSet<Integer> set = new HashSet<>();for(int j = i + 1; j < nums.length; j++) {//对三元组b去重如[0, 0, 0, 0]if(j > i + 2 && (nums[j] == nums[j - 1] && nums[j - 1] == nums[j - 2]))continue;int target = -nums[i] - nums[j];if(set.contains(target)) {res.add(Arrays.asList(nums[i], nums[j],target));//对c去重set.remove(target);}else {set.add(nums[j]);}}}return res;}

双指针法
1.首先对数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
2.依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
3.如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
4.如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
先不看代码实现一下吧!

public List<List<Integer>> threeSum2(int[] nums) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(nums);for(int i = 0; i < nums.length; i++) {//第一个数大于0了,就不会有满足题目要求的数了if(nums[i] > 0)break;//对数a去重if(i > 0 && nums[i] == nums[i - 1])continue;int left = i + 1;int right = nums.length - 1;while(left < right) {int sum = nums[i] + nums[left] + nums[right];if(sum > 0) {right--;}else if(sum < 0) {left++;}else {res.add(Arrays.asList(nums[i], nums[left], nums[right]));while(left < right && nums[left + 1] == nums[left])left++;while(left < right && nums[right - 1] == nums[right])right--;left++;right--;}}}return res;}

啊,竟然一摸一样,时间复杂度进一步降低了!代码的世界真奇妙!这次的时间复杂度小于O(n^2)了。

自己实现过程中遇到哪些困难:

将数组转换为List的函数不熟悉,不会运用。Arrays.asList().

454.四数相加II

建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。

'题目链接/文章讲解/视频讲解:

看到题目的第一想法:

和之前的三数之和的想法类似
1.设置一个map,map的key表示nums[i]的值,value表示出现的次数
2.用map存储其中的一个数组
3.三层for循环,分别求得nums[i],nums[j]和nums[k],然后判断map中是否有0-nums[i]-nums[j]
4.如果map中存在,放入list中,为了去重,排序后放入set集合中
可想而知这个时间复杂度是很高的!但在leetcode上能够通过。期间我也遇到了一个整数溢出的问题。是之前三数之和没出现的。

package com.second.day7;import java.util.*;public class FourSum_18 {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> result = new ArrayList<>();Set<List<Integer>> set = new HashSet<>();//key存储nums[i]的值,value存储值的个数HashMap<Integer, Integer> map = new HashMap<>();for(int num : nums) {map.put(num, map.getOrDefault(num, 0) + 1);}for(int i = 0; i < nums.length - 2; i++) {map.put(nums[i], map.get(nums[i]) - 1);for(int j = i + 1; j < nums.length - 1; j++) {map.put(nums[j], map.get(nums[j]) - 1);for(int k = j + 1; k < nums.length; k++) {map.put(nums[k], map.get(nums[k]) - 1);long num4 = (long)target - ((long)nums[i] + nums[j] + nums[k]);if(num4 < Integer.MIN_VALUE || num4 > Integer.MAX_VALUE) {map.put(nums[k], map.get(nums[k]) + 1);continue;}List<Integer> list = new ArrayList<>();if(map.containsKey((int)num4) && map.get((int)num4) > 0) {list.add(nums[i]);list.add(nums[j]);list.add(nums[k]);list.add((int)num4);Collections.sort(list);set.add(list);}map.put(nums[k], map.get(nums[k]) + 1);}map.put(nums[j], map.get(nums[j]) + 1);}map.put(nums[i], map.get(nums[i]) + 1);}for(List<Integer> lists : set) {result.add(lists);}return result;}public static void main(String[] args) {int[] nums = new int[]{1, 0, -1, 0, -2, 2};FourSum_18 demo = new FourSum_18();demo.fourSum(nums, 0);}
}

看完代码随想录之后的想法:

双指针法:
(1)和三树之和的双指针法思想是一致的,但在一些细节上是不同的。因为这个题目的target是不固定的,不能直接在第一次循环的时候nums[i] > target就可以不循环,因为如果target为-10,-4>-10,nums[i]后面还有负数的话就有可能满足题目要求。
(2)四数之和相对于三数之和是有两层for循环,同样也设置left和right,然后找nums[i]+nums[j]+nums[left]+nums[right]=target的情况。
(3)用双指针法,三数之和的时间复杂度从O(n ^ 3) 降到了O(n ^ 2),四数之和的时间复杂度从O(n ^ 4) 降到了O(n ^ 3).之后的五数之和,六数之和都可以用同样的方法。
先不看卡哥代码,自己先实现一下吧。

public List<List<Integer>> fourSum1(int[] nums, int target) {List<List<Integer>> result = new ArrayList<>();//对数组进行排序Arrays.sort(nums);for(int i = 0; i < nums.length - 3; i++) {//剪枝if(nums[i] > target && (nums[i] > 0 || target > 0))break;//去重if(i > 0 && nums[i] == nums[i - 1])continue;for(int j = i + 1; j < nums.length - 2; j++) {//剪枝if((nums[i] + nums[j]) > target && (nums[i] + nums[j] > 0 || target > 0))continue;//去重if(j > i + 1 && nums[j] == nums[j - 1])continue;int left = j + 1;int right = nums.length - 1;while(left < right) {int sum = nums[i] + nums[j] + nums[left] + nums[right];if(sum > target) {right--;}else if(sum < target) {left++;}else {result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));while(left < right && nums[left] == nums[left + 1])left++;while(left < right && nums[right] == nums[right - 1])right--;left++;right--;}}}}return result;}

注意:上面的代码没有做整数溢出处理也能过得原因是剪枝过程中规避了溢出。最好还是要像卡哥一样,求sum是把他转为long

自己实现过程中遇到哪些困难:

[-1000000000,-1000000000,-1000000000,-1000000000]
294967296
整数溢出处理:
我第一做的时候是先把他转为long,然后判断这个数是否在int的范围内,没有在的话就不符合条件,跳过去。

long num4 = (long)target - ((long)nums[i] + nums[j] + nums[k]);
if(num4 < Integer.MIN_VALUE || num4 > Integer.MAX_VALUE) {map.put(nums[k], map.get(nums[k]) + 1);continue;
}

好吧这种方法很笨。
还是双指针法好。

今日收获,记录一下自己的学习时长:

今天的题我虽然都硬写出来了,真就是硬写出来的,时间复杂度都太高了,好吧多数之和没有想到可以用双指针法进行剪枝与去重。今天的博客写了我两天。脚踏实地的,不骗自己。
今天写了四个题:
454.四数相加II , 383. 赎金信, 15. 三数之和 ,18. 四数之和
代码:4h
博客:1.5h
想干就干,要干就干的漂亮,即使没有人为你鼓掌,至少还能够勇敢的自我欣赏。

代码随想录算法训练营第七天| 哈希表理论基础 ,454.四数相加II, 383. 赎金信, 15. 三数之和, 18. 四数之和相关推荐

  1. 代码随想录算法训练营day6| 454.四数相加II 383.赎金信 15.三数之和 18.四数之和

    代码随想录算法训练营day6| 454.四数相加II 383.赎金信 15.三数之和 18.四数之和 LeetCode 454 四数相加II 题目链接: 454.四数相加II class Soluti ...

  2. 代码随想录算法训练营第七天|454.四数相加II ● 383. 赎金信 ● 15. 三数之和 ● 18. 四数之和

    一.454.四数相加II 力扣 思路:第一眼还没反应过来,真是缺练.在四个数组中分别寻找,可以先把前两个数组的和先存入map中,再计算后两个数组元素的和,看一下相反数在map中出现没有,出现过就res ...

  3. 代码随想录算法训练营第6天 | 454. 四数相加 II 383. 赎金信 15. 三数之和 18. 四数之和

    一.Leetcode 454. 四数相加 II 相当于两数相加.但是呢很巧妙的是,卡哥在遍历CD数组时把查哈希表的方法融入了进去.学习一下. 二.Leetcode 383. 赎金信 更简单了,主要是审 ...

  4. 代码随想录算法训练营第七天|454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

    今日学习的文章和视频链接 454文章链接: link 454视频讲解链接: link 383文章链接: link 383视频暂无讲解 15文章链接: link 15视频讲解链接: link 18文章链 ...

  5. 代码随想录算法训练营第七天 | 454. 四数相加 II、383. 赎金信、15. 三数之和、18. 四数之和

    454. 四数相加 II 题解及想法 通过两个for循环先遍历a和b,key放a和b两数之和,value 放a和b两数之和出现的次数,再通过两个for循环遍历c和d,如果0-(c+d) 在map中出现 ...

  6. 代码随想录算法训练营第七天| 454.四数相加II,383. 赎金信,15. 三数之和,18. 四数之和

    Leetcode 454.四数相加II 思路分析: 本题直观的想法是采取暴力法,四数相加就用四层for循环.虽然能得到结果,但时间复杂度为o(n4),当数组长度较大时,Leetcode便提示超时.该方 ...

  7. 代码随想录算法训练营第七天|454、四数相加Ⅱ 383、赎金信15、三数之和18、四数之和

    454.四数相加Ⅱ.383.赎金信.15.三数之和四数之和 四数相加 对于四数相加,我们可以定义一个map用来记录nums1与nums2的和对应次数,再遍历nums3与nums4,如果存在c与d使得a ...

  8. 代码随想录算法训练营第七天| 454.四数相加II 、383. 赎金信 、15. 三数之和 、18. 四数之和 。

    454.四数相加II 题目链接 给你四个整数数组 nums1.nums2.nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <= i ...

  9. 代码随想录算法训练营第七天| 454.四数相加II 、383. 赎金信、15. 三数之和、18. 四数之和

    454. 四数相加 II 题目: 给你四个整数数组 nums1.nums2.nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <= ...

最新文章

  1. 如何积累自己的技术认知
  2. linux日志文件存放目录,Log4j 日志文件Linux/Mac/Windows通用存放位置设置方法
  3. 微信小程序开发 Request Headers: Provisional headers are shown
  4. ffmpeg 命令画中画效果
  5. 北大教授:到底什么才是有效的教育?
  6. 怎么在HTML上显示数据库的表格,在预定义的html表格中显示数据库表格记录
  7. window.location.href跳转无效 IE Bug【转载】
  8. devc 能优化吗_Devc 、一元多项式的加法、减法、乘法的实现 【问题描述】 设有 联合开发网 - pudn.com...
  9. c#操作txt文本之查询
  10. 整好用版NI Circuit Design Suite Power Pro 11.0
  11. usb无线网卡断线后找不到网络
  12. 【html5期末大作业】基于HTML仿QQ音乐官网网站
  13. 利用stm32f103c8t6实现对WS2812的控制(从硬件出发)
  14. lopatkin俄大神精简Windows 10 Enterprise 19041.331 20H1 Release x86-x64 EN-RU PIP
  15. android NV21裁剪算法
  16. e470c拆机图解全拆 thinkpad_联想ThinkPad T470拆机图解教程
  17. EOJ 3452 唐纳德先生和假骰子
  18. hbw-utils - BaseUtils
  19. SM4分组密码算法介绍
  20. js在线写作文本编辑器插件Writty

热门文章

  1. RTU-518G 研究
  2. 用学生邮箱注册JetBrains官网账号
  3. 奥迪J518方向锁通病维修
  4. 酱狗的杂七杂八(叁)
  5. 男人最喜欢女人说的五句话
  6. kernel error 解决办法
  7. 大学c语言毕业设计题目,关于毕业季的作文题目
  8. 小甲鱼零基础学python笔记 P24 递归:这帮小兔崽子
  9. 关于IMX双通道LVDS 的深入讲解
  10. 从“地球漫游计划”看城市,如何利用视频监控让城市直播更简单?