标题: 【leetcode】双指针(二)
水墨不写bug
正文开始:
(一)总和为目标值的两个数
购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。示例 1:
输入:price = [3, 9, 12, 15], target = 18
输出:[3,15] 或者 [15,3]
示例 2:输入:price = [8, 21, 27, 34, 52, 66], target = 61
输出:[27,34] 或者 [34,27]
提示:1 <= price.length <= 10^5
1 <= price[i] <= 10^6
1 <= target <= 2*10^6
首先是暴力算法:
枚举出所有的两个数形成的集合,判断两数之和是否等于target即可;
优化:
a,初始化left , right 分别指向数组的左右两端(这里不是真正意义上的指针,⽽是数组的下标)
b, 当 left < right 的时候,⼀直循环
i、当 nums[left] + nums[right] == target 时,说明找到结果,记录结果,并且返回;
ii、当 nums[left] + nums[right] < target 时:
• 对于升序数组nums[left] ,此时 nums[right] 相当于是nums[left] 能碰到的最⼤值。如果此时不符合要求,说明在这个数组里面,没有别的数满足 nums[left] 的要求了。
因此,我们可以⼤胆舍去这个数,让 left++ ,去比较下⼀组数据;
• 那对于 nums[right] ⽽⾔,由于此时两数之和是⼩于⽬标值的, nums[right] 还可以选择⽐nums[left] ⼤的值继续努⼒达到⽬标值,因此 right 指针我们暂时不动;
iii、 当 nums[left] + nums[right] > target 时,同理我们可以舍去nums[right] 。让 right-- ,继续⽐较下⼀组数据,⽽ left 指针不变(因为它还是可以去匹配比nums[right] 更⼩的数的)。
(一)三数之和
给你一个整数数组
nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != k
,同时还满足nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为
0
且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。示例 2:
输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。示例 3:
输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-10^5 <= nums[i] <= 10^5
其实在两数之和的基础上,三数之和就显得简单了许多,如果直接给你一道三数之和,也许你无法想到正确的符合时间复杂度的算法,但是我们在了解了两数之和之后,就会发现三数之和其实是两数之和的一般化的情况:
算法思想:
对于任意数组,我们可以先固定一个数,然后在剩余的数组中找到两个数,并使这两个数的和等于固定的数的相反数即可;
这也就是降维思想来解决问题,当我们理解了三数之和的解法之后,就会发现两数之和的查找其实就是三数之和固定一个固定值后对剩余两数的查找;
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ret;int left = 0,right = 0;//排序sort(nums.begin(),nums.end());int n = nums.size();for(int target = 0 ;nums[target] <= 0 && (target < n-1) ; target++ ){if( ( target > 0)&&nums[target] == nums[target - 1] ){continue;}for(left = target+1,right = n-1;left < right; ){if(nums[left] + nums[right] + nums[target] < 0 && (left < right)) {++left;}else if(nums[left] + nums[right] + nums[target] > 0 && (left < right)) {--right;}else{//插入数据ret.push_back( {nums[left],nums[right],nums[target]} );//处理连续跨越问题和越界问题while(nums[++left] == nums[left-1] && (left < right));while(nums[--right] == nums[right+1] && (left < right));}}}return ret;}
};
(二)四数之和
给你一个由
n
个整数组成的数组nums
,和一个目标值target
。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]示例 2:
输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-10^9 <= nums[i] <= 10^9
-10^9 <= target <= 10^9
在两数之和与三数之和的思路延续上,我们也可以类似的类比出四数之和的解决方法:
思路:
对于任意一个数组找到和为target的数,
可以先固定一个数组中的数a,在除此数a的其余数组中找到target-a的数;
接下来固定一个数b,在除此数b和数a的其余数组中找到target-a-b的数;
再固定一个数c,在除此数c和数b和数a的其余数组中找到target-a-b-c的数;
现在这个问题就退化成了二数之和的问题:
对任意数组,在除此数c和数b和数a的其余数组中找到target-a-b-c的数。
这样我们将这个问题不断简化,就将复杂的问题转化为简单的我们可以解决的问题。
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;//排序sort(nums.begin(),nums.end());int n = nums.size() ;//找和为target的4个数for(int a = 0;a < n-1;){//找和为target-nums[a]的3个数for(int b = a+1;b < n-1;){//找和为target-nums[a]-nums[b]的2个数,这时可利用双指针for(int left = b+1,right = n-1;left < right;){long sum = (long)nums[a] + (long)nums[b] + (long)nums[left] + (long)nums[right];if( sum < target && (left < right)){++left;}else if(sum > target && (left < right)){--right;}else {ret.push_back( { nums[a] , nums[b] , nums[left] , nums[right] } );while(nums[++left] == nums[left-1] && (left < right));while(nums[--right] == nums[right+1] && (left < right));}}b++;while(nums[b] == nums[b-1] && (b < n-1)) b++;}a++;while(nums[a] == nums[a-1] && (a < n-1 )) a++;}return ret;}
};
回顾
目录
(一)总和为目标值的两个数
(一)三数之和
(二)四数之和
完~
未经作者同意禁止转载