一文带你秒杀三个求和问题php
今天为你们带来三道求和问题,经过文字,图画,动图为你们解析,很容易就能读懂,每道题目都是经典题,你们快来打卡吧。java
题目来源:leetcode 1.两数之和(简单) 15.三数之和(中等) 18.四数之和(中等)git
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。程序员
你能够假设每种输入只会对应一个答案。可是,数组中同一个元素不能使用两遍。github
示例:算法
给定 nums = [2, 7, 11, 15], target = 9数组
由于 nums[0] + nums[1] = 2 + 7 = 9
因此返回 [0, 1]微信
题目很容易理解,即让查看数组中有没有两个数的和为目标数,若是有的话则返回两数下标,咱们为你们提供两种解法双指针(暴力)法,和哈希表法框架
哈希表的作法很容易理解,咱们只需经过一次循环便可,假如咱们的 target 值为 9,当前指针指向的值为 2 ,咱们只需从哈希表中查找是否含有 7,由于9 - 2 =7 。若是含有 7 咱们直接返回便可,若是不含有则将当前的2存入哈希表中,指针移动,指向下一元素。注: key 为元素值,value 为元素索引。spa
动图解析:
是否是很容易理解,下面咱们来看一下题目代码。
class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer,Integer> map = new HashMap<Integer,Integer>(); 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 new int[0]; } }
双指针(L,R)法的思路很简单,L指针用来指向第一个值,R指针用来从第L指针的后面查找数组中是否含有和L指针指向值和为目标值的数。见下图
例:绿指针指向的值为3,蓝指针须要在绿指针的后面遍历查找是否含有 target - 3 = 2的元素,若含有返回便可。
class Solution { public int[] twoSum(int[] nums, int target) { if(nums.length < 2){ return new int[0]; } int[] rearr = new int[2]; //查询元素 for(int i = 0; i < nums.length; i++){ for(int j = i+1; j < nums.length; j++ ){ if(nums[i] + nums[j] ==target){ rearr[0] = i; rearr[1] = j; } } } return rearr; } }
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出全部知足条件且不重复的三元组。
注意:答案中不能够包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
知足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
这个题目算是对刚才题目的升级,刚才题目咱们是只需返回一个例子便可,可是这个题目是让咱们返回全部状况,这个题目咱们须要返回三个数相加为 0 的全部状况,可是咱们须要去掉重复的三元组(算是该题的核心),因此这个题目仍是挺有趣的,你们记得打卡呀。
咱们这个题目的哈希表解法是很容易理解的,咱们首先将数组排序,排序以后咱们将排序过的元素存入哈希表中,咱们首先经过两层遍历,肯定好前两位数字,那么咱们只须要哈希表是否存在符合状况的第三位数字便可,跟暴力解法的思路相似,很容易理解,可是这里咱们须要注意的状况就是,例如咱们的例子为[-2 , 1 , 1],若是咱们彻底按照以上思路来的话,则会出现两个解,[-2 , 1 , 1]和[1 , 1, -2]。具体缘由,肯定 -2,1以后发现 1 在哈希表中,存入。肯定 1 ,1 以后发现 -2 在哈希表中,存入。因此咱们须要加入一个约束避免这种状况,那就是咱们第三个数的索引大于第二个数时才存入。
上面这种状况时是不能够存入的,由于咱们虽然在哈希表中找到了符合要求的值,可是 -2 的索引为 0 小于 2 因此不能够存入。
class Solution { public List<List<Integer>> threeSum(int[] nums) { if(nums.length < 3){ return new ArrayList<>(); } //排序 Arrays.sort(nums); HashMap<Integer,Integer> map = new HashMap<>(); List<List<Integer>> resultarr = new ArrayList<>(); //存入哈希表 for(int i = 0; i < nums.length; i++){ map.put(nums[i],i); } Integer t; int target = 0; for(int i = 0; i < nums.length; ++i){ target = -nums[i]; //去重 if(i > 0 && nums[i] == nums[i-1]){ continue; } for(int j = i + 1; j < nums.length; ++j){ if(j > i+1 && nums[j] == nums[j-1]){ continue; } if((t = map.get(target - nums[j])) != null){ //符合要求的状况,存入 if(t > j){ resultarr.add(new ArrayList<> (Arrays.asList(nums[i], nums[j], nums[t]))); } else{ break; } } } } return resultarr; } }
若是咱们将上个题目得指针解法称作是双指针的话,那么这个题目用到的方法就是三指针,由于咱们是三数之和嘛,一个指针对应一个数,下面咱们看一下具体思路,其实原理很简单,咱们先将数组排序,直接 Arrays.sort() 解决,排序以后处理起来就很容易了。下面咱们来看下三个指针的初始位置。
初始状况见上图,咱们看当前状况,三数之和为 -3 ,很显然不是 0 ,那么咱们应该怎么作呢?
咱们设想一下,咱们当前的三数之和为 -3 < 0 那么咱们若是移动橙色指针的话则会让咱们的三数之和变的更小,由于咱们的数组是有序的,因此咱们移动橙色指针(蓝色不动)时和会变小,若是移动蓝色指针(橙色不动)的话,三数之和则会变大,因此这种状况则须要向右移动咱们的蓝色指针,找到三数之和等于 0 的状况进行保存,若是三数之和大于 0 的话,则须要移动橙色指针,途中有三数之和为 0 的状况则保存。直至蓝橙两指针相遇跳出该次循环,而后咱们的绿指针右移一步,继续执行上诉步骤。可是这里咱们须要注意的一个细节就是,咱们须要去除相同三元组的状况,咱们看下面的例子。
这里咱们发现 0 - 1 + 1 = 0,当前状况是符合的,因此咱们须要存入该三元组,存入后,蓝色指针向后移动一步,橙色指针向前移动一步,咱们发现仍为 0 -1 + 1 = 0 仍然符合,可是若是继续存入该三元组的话则不符合题意,因此咱们须要去重。这里能够借助HashSet可是效率太差,不推荐。这里咱们可使用 while 循环将蓝色指针移动到不和刚才相同的位置,也就是直接移动到元素 0 上,橙色指针一样也是。则是下面这种状况,这样咱们就实现了去重,而后继续判断当前三数之和是否为 0 。
动图解析:
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> arr = new ArrayList<List<Integer>>(); if(nums.length < 3){ return arr; } //排序 Arrays.sort(nums); if(nums[0] > 0){ return arr; } for(int i = 0; i < nums.length-2; i++){ int target = 0 - nums[i]; //去重 if(i > 0 && nums[i] == nums[i-1]){ continue; } int l = i+1; int r = nums.length - 1; while(l < r){ if(nums[l] + nums[r] == target){ //存入符合要求的值 arr.add(new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[r]))); //这里须要注意顺序 while(l < r && nums[l] == nums[l+1]) l++; while(l < r && nums[r] == nums[r-1]) r--; l++; r--; } else if(nums[l] + nums[r] > target){ r--; } else{ l++; } } } return arr; } }
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出全部知足条件且不重复的四元组。
注意:
答案中不能够包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
知足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
咱们已经完成了两数之和和三数之和,这个题目应该就手到擒来了,由于咱们已经知道这类题目的解题框架,两数之和呢,咱们就先固定第一个数 ,而后移动指针去找第二个符合的,三数之和,固定一个数,双指针去找符合状况的其余两位数,那么咱们四数之和,也能够先固定两个数,而后利用双指针去找另外两位数。因此咱们来搞定他吧。
三数之和是,咱们首先肯定一个数,而后利用双指针去找另外的两个数,咱们在这个题目里面的解题思路是须要首先肯定两个数而后利用双指针去找另外两个数,和三数之和思路基本一致很容易理解。咱们具体思路能够参考下图。
这里须要注意的是,咱们的 target 再也不和三数之和同样为 0 ,target 是不固定的,因此解题思路不能够彻底照搬上面的题目。另外这里也须要考虑去重的状况,思路和上题一致。
上图则为咱们查找到一个符合条件的四元组的状况,查找成功以后,下一步移动蓝色指针,从新定义绿蓝指针,继续执行上面步骤。
动图解析:
class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { if(nums.length < 4){ return new ArrayList<>(); } Arrays.sort(nums); List<List<Integer>> arr = new ArrayList<>(); for(int i = 0; i < nums.length-3; ++i){ if(i > 0 && nums[i] == nums[i-1]){ continue; } for(int j = i+1; j < nums.length-2; j++){ if(j > i+1 && nums[j] == nums[j-1]){ continue; } int l = j+1; int r = nums.length-1; while(l < r){ int sum = nums[i] + nums[j] + nums[l] + nums[r]; if(sum == target){ //存入 arr.add(new ArrayList<> (Arrays.asList(nums[i], nums[j], nums[l], nums[r]))); //去重 while(l < r && nums[l] == nums[l+1]){ l++; } while(l < r && nums[r] == nums[r-1]){ r--; } l++; r--; } else if(sum > target){ r--; } else{ l++; } } } } return arr; } }
经过上面的三个例子,你们是否是把此类求和问题摸的透透的啦,若是能感受到这个文章写的很用心的话,能给您带来一丢丢帮助的话,能麻烦您给这个文章点个赞吗?这样我就巨有动力写下去啦。
另外你们若是须要其余精选算法题的动图解析,你们能够微信关注下袁厨的算法小屋,我是袁厨一个酷爱作饭因此本身考取了厨师证的菜鸡程序员,会一直用心写下去的,感谢支持!