目录
两数之和
这道题本质上是找出数组中任意两数之和为 target,我们可以使用两层 for 循环暴力的找出这两个数,但这样会超时。
可以注意到,题目提供的数组是升序的,升序的数组中,最小值在数组的最左边,最大值在数组的最右边,假设 left 为数组的左端,初始化为 0,right 为数组的右端,初始化为 price.size() -1,
以示例2为例,在暴力解法中,比如先固定8,接着 for 循环遍历 8 后面的数字,一个个求和,看它们的和是否等于 target,但是我们没必要一个个求和,
1、如下图,如果 price[ left ] + price[ right ] > target ,说明 price[ right ] 和 比 price[ left ] 大的数的和一定大于 target,那么在升序数组中, price[ left ] 后面的数所以就没有必要一个个遍历并与 price[ right ] 求和了,此时 --right,去比较下一组数
2、如果 price[ left ] + price[ right ] < target ,和上面同理,price[ left ] 和比 price[ right ] 小的数的和一定小于 target,此时 ++left,去比较下一组数
3、以此类推,直到 price[ left ] + price[ right ] = target 为止
4、需要注意,left 必须小于 right,否则数组里的数重复遍历了,如果 left 和 right 相遇了,说明这个数组里面没有两数之和为 target
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target) {
int left=0;
int right=price.size()-1;
vector<int> v;
while(left<right)
{
if(price[left]+price[right]>target)
--right;
else if(price[left]+price[right]<target)
++left;
else
{
v.push_back(price[left]);
v.push_back(price[right]);
break;
}
}
return v;
}
};
有效三角形的个数
611. 有效三角形的个数 - 力扣(LeetCode)https://leetcode.cn/problems/valid-triangle-number/description/
假设三角形的三条边分别为 a , b , c,三角形成立的条件为 a+b > c ,a+c>b , b+c>a 。
我们需要经过三次判断,才可以看出 a , b , c 是否构成三角形,如何减少判断的次数?
假设 a , b , c 已经升序排序,a 为最小值,c 为最大值,则一定有 c>a , c>b ,而 a , b , c 都是正数,c + 正数 > a, c + 正数 >b 显然成立,那么,这时候只需要判断 a+b> c是否成立,就可以判断出 a , b , c 能否构成三角形,此时只需要 1次排序 + 1次判断
如图,在升序数组中,假设最大值 5 为 c,nums[ left ] 为 a , nums[ left ] 为 b,和两数之和同理,不同的是,这一次需要寻找比 target 大的两个数:
1、如果 nums[ left ] + nums[ right ] <= c,则这三个数不构成三角形,此时 ++left,
2、如果 nums[ left ] + nums[ right ] > c,则这三个数构成三角形,比 nums[ left ] 大的数和 nums[ right ] 相加一定大于 c,此时能构成三角形的三元组个数恰好为 right - left ([2,4,5](第一个2),[2,4,5](第二个2),[3,4,5]),此时 --right,去寻找其他的三元组
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int ret=0;//记录个数
for(int i=nums.size()-1;i>1;--i)
{
int left=0;
int right=i-1;
while(left<right)
{
if(nums[left]+nums[right]>nums[i])//构成三角形
{
ret+=(right-left);
--right;
}
else//不构成三角形
++left;
}
}
return ret;
}
};
三数之和
15. 三数之和 - 力扣(LeetCode)https://leetcode.cn/problems/3sum/description/
和有效三角形的个数同理,这次我们需要找的是两数之和为 -target 的数,而且这三元组之间不可以重复(示例1)。
以示例1为例:对示例1的数组进行排序后,先以最左端的元素开始,那么 target 为 4,我们需要找出什么时候 nums[ left ] 和 nums[ right ] 的和为 target,
1、如果 nums[ left ] + nums[ right ] > target,--right
2、如果 nums[ left ] + nums[ right ] < target,++left
如果两数之和一直不等于 target,但是 left = right,那么说明数组中不存在两数之和为 target,需要换新的 target 了
3、如果 nums[ left ] + nums[ right ] = target,那么把此时访问的三个数存储在vector中,此时 left 和 right 都需要更新( ++left , --right )
如果left/right更新之后,仍然和上一次访问的 nums[ left ] / nums[ right ] 相同,因为题目条件,三元组不可以重复,所以需要继续更新 left / right,直到和上一次访问的nums[ left ] / nums[ right ] 不同为止:
4、更新 target 之后,和上次的 target 相同,这时候再去找两数之和等于 target ,就会出现重复的三元组,所以需要迭代至和上一个 target 不同为止
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());//排序
int i=0;
vector<vector<int>> vv;//返回值
while(i<nums.size() && nums[i]<=0)
{
int left=i+1;
int right=nums.size()-1;
while(left<right)
{
if(nums[left]+nums[right]+nums[i]>0)
{
--right;
}
else if(nums[left]+nums[right]+nums[i]<0)
{
++left;
}
else
{
vv.push_back({nums[left],nums[right],nums[i]});
++left;
--right;
//去重左指针
while(left<right && nums[right]==nums[right+1]) { --right;}
//去重右指针
while(left<right && nums[left]==nums[left-1]) {++left; }
}
}
//去重target
i++;
while(i<nums.size() && nums[i]==nums[i-1]) {++i; }
}
return vv;
}
};
四数之和
18. 四数之和 - 力扣(LeetCode)https://leetcode.cn/problems/4sum/description/
四数之和可以划分为三数之和、两数之和,
比如先固定 - 4,问题变成在红色框的数中找三数之和为 target1 = target -(- 4) ,
在找三数之和时,同样固定 -4,问题变成在蓝色框的数中找两数之和为 target2 = target1 -(- 4),
在求解四数之和时,同样需要和三数求和一样,去掉重复的解
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> vv;
for(int i=0;i<nums.size();)
{
long long target1=(long long)target-nums[i];
//三数之和为target1
for(int j=i+1;j<nums.size();)
{
long long target2=(long long)target1-nums[j];
//两数之和为target2
int left=j+1;
int right=nums.size()-1;
while(left<right)
{
if(nums[left]+nums[right]>target2) --right;
else if(nums[left]+nums[right]<target2) ++left;
else{
vv.push_back({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;
}
}
//去重target2
++j;
while(j<nums.size() && nums[j]==nums[j-1]) ++j;
}
//去重target1
++i;
while(i<nums.size() && nums[i]==nums[i-1]) ++i;
}
return vv;
}
};