之所以放在一起是因为,"四数之和"的解题方法基本与"三数之和"一致

由此我们可以推出n数之和的解法

本质上,我们只是使用双指针的方法降低此类问题的时间复杂度

当然用哈希法也可以解,那就是另外的故事了

三数之和

力扣题目链接(opens new window)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

初见分析

很像四数相加||啊

那题是用了map去解决的,于是照猫画虎开始做

做到最后会发现,题目要求的是返回由数组中三个相加为0的元素构成元组

这里就的有问题了,如果按照原来的思路

那么如何保存符合条件的遍历值的下标?并且还没有重复值

思路

不整哈希法了,用双指针法解会好理解一些,过程如下图所示:

题目要求是返回三个相加为0的数,即: a+b+c = 0,与下标无关

初始化指针

还是分开处理,初始化三个指针

a 对应 i(固定值)

b 对应 left

c 对应 right

这里在使用双指针前需要对数组进行排序(用sort就行不用自己写)

遍历数组

在遍历过程中会出现以下几种情况:

0、nums[i] > 0

​ 这种情况的话就直接return,因为如果第一个数是大于0的数,那么后面的数肯定都是正数(因为提前经过排序),则不可能再计算得到0

1、nums[i] + nums[left] + nums[right] > 0

i 对应的数是最小的,不用管,主要看 bc (所以才叫双指针而不是三指针)

​ 因为已经排好序,所以要让整体值变小进而靠近0,需要让最大的值即right向左移动

2、nums[i] + nums[left] + nums[right] < 0

​ 同理,要让整体值变大,最小的值left要向右移动

3、nums[i] + nums[left] + nums[right] = 0

​ 找到目标三元组,保存

大致结构如下:

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
...
for (int i = 0; i < nums.size(); i++) { if (nums[i] > 0) {
return result;
}
//去重
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
} int left = i + 1;
int right = nums.size() - 1; while (right > left) {
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
//先保存,再去重
}
}
}
}
};
去除重复值

还是以a+b+c为例

去除a中的重复值

其中,i 为最外层循环,每当 i 更新(向后移动)时,需要对其进行去重操作

注意,去重时需要使用num[ i ]和num[ i + 1 ]来比较,而不是使用num[ i - 1 ]和num[ i ]

后者会漏掉某些情况,例如

{-1, -1, 2}
↑ ↑
i-1 i

此时,num[ i - 1 ] = num[ i ],理应直接返回,不再继续遍历

但是这样就会把{-1, -1, 2}这种情况漏掉

去除b、c中的重复值

当第一次满足nums[i] + nums[left] + nums[right] = 0条件时,我们先要对本次的数做保存

之后再满足条件就要对值进行去重操作

原理和对 a 的做法是一样的,仍然是拿当前遍历值与它的下一个值做比较,如果相等就继续移动对应的指针

代码

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;//初始化一个vec用来存放结果
//先对数组进行排序
sort(nums.begin(),nums.end());
//遍历数组(最外层,i)
for(int i = 0; i < nums.size(); i++){
//判断i是否大于零
//大于零直接返回了
//这种认为规避多余运算的处理称为"剪枝"
if(nums[i] > 0){
return res;
}
//对i进行去重
if(i > 0 && nums[i] == nums[i - 1]){//不要忘了i > 0的条件,因为第一次是不需要去重的
continue;//i往后移
}
//初始化双指针(代表b、c)
int left = i + 1;//指向i的后一位
int right = nums.size() - 1;//指向数组末尾
while(left < right){ //不能取等于号,那样b和c就是同一个值了,不满足三个数的要求
//判断是否找到满足条件的三个值
//三种情况
if(nums[i] + nums[left] + nums[right] > 0){
//右指针左移,减小整体值
right--;
}else if(nums[i] + nums[left] + nums[right] < 0){
//左指针右移,增大整体值
left++;
}else{//等于的情况,找到三个目标值
//先保存第一次满足条件的结果
res.push_back(vector<int>{nums[i], nums[left], nums[right]});
//第二次就开始去重
// //b去重(left)
// if(left < right && nums[left] == nums[left + 1]){
// left++;
// }
// //c去重(right)
// if(left < right && nums[right - 1] == nums[right]){
// right--;
// }
//这里不要用if
//将所有可能的b、c值都做去重判断,直到不满足left < right,结束本次遍历,更新i进行下次遍历
while(left < right && nums[left] == nums[left + 1]){
left++;
}
while(left < right && nums[right - 1] == nums[right]){
right--;
}
//如果没找到重复值,左右指针均向中心移动
left++;
right--;
}
}
}
return res;
}
};
易错点
1、初始化vector

因为这个vector是用来存放一个三个元素的数组的,所以初始化的时候数据类型应该是vector,而不是int

解题模板也有提示,注意看

2、去重的时机

无论是对a还是b、c进行去重,第一次得到的结果都是不用进行去重的

3、保存结果后的去重操作

当第一次满足条件时:

{-1, -1, -1, 3, 2, 2}
↑ ↑ ↑
i left right

我们得到了

这时left、right移动

{-1, -1, -1, 3, 2, 2}
↑ ↑ ↑
i left right

发现nums[left] == nums[left + 1]并且nums[right - 1] == nums[right]

显然是出现了重复,此时需要跳过当前left、right指向的值

直到不出现这种重复情况为止,因此这里必须使用while而不是if

四数之和

力扣题目链接(opens new window)

题意:给定一个包含 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] ]

思路

和三数之和的思路一样,都是用双指针法去解

无非就是再多套一层循环

解决三/四数之和,或者说n数之和问题,本质上是利用双指针的方法去降低时间复杂度

四数之和的双指针解法是两层for循环nums[k] + nums[j]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[j] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n2),四数之和的时间复杂度是O(n3)

简单来说,这里的固定值有两个: k 和 j

代码

class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
//先对数组进行排序
sort(nums.begin(),nums.end()); //剪枝操作
for(int k = 0; k < nums.size(); k++){
//因为target是自定义的,可以dayu0也可以小于0,所以不能单纯的通过nums[k] > target来剪枝
//
if (nums[k] > target && nums[k] >= 0) {
break; // 这里使用break,统一通过最后的return返回
} //对k进行去重
if(k > 0 && nums[k] == nums[k - 1]){//不要忘了k > 0的条件,因为第一次是不需要去重的
continue;//i往后移
} for(int j = k + 1; j < nums.size(); j++){
// 2级剪枝处理,同理
if (nums[k] + nums[j] > target && nums[k] + nums[j] >= 0) {
break;
}
//对j进行去重
if(j > k + 1 && nums[j] == nums[j - 1]){
continue;//j往后移
}
//初始化双指针(代表b、c)
int left = j + 1;//指向j的后一位
int right = nums.size() - 1;//指向数组末尾
while(left < right){
if((long)nums[k] + nums[j] + nums[left] + nums[right] > target){
right--;
}else if((long)nums[k] + nums[j] + nums[left] + nums[right] < target){
left++;
}else{
res.push_back(vector<int>{nums[k], nums[j], nums[left], nums[right]});
//去重
//将所有可能的b、c值都做去重判断,直到不满足left < right,结束本次遍历
while((long) left < right && nums[left] == nums[left + 1]){
left++;
}
while((long) left < right && nums[right - 1] == nums[right]){
right--;
}
//如果没找到重复值,左右指针均向中心移动
left++;
right--;
}
}
}
}
return res;
}
};
注意点
1、剪枝操作

不能使用nums[k] > target直接进行剪枝操作

例如有:[-4, -1, 0, 0],target = -5

此时nums[k] = -4,nums[k]确实大于target

如果按照上面的条件进行剪枝,该结果集会被直接跳过,显然是不行的

因此这种情况下就不要剪枝了,当target大于0时我们再使用nums[k] > target来判断是否剪枝

综上,本题中的剪枝条件应该改为`nums[k] > target && nums[k] >= 0

【LeetCode】三数之和+四数之和(双指针)的更多相关文章

  1. 【LeetCode】 454、四数之和 II

    题目等级:4Sum II(Medium) 题目描述: Given four lists A, B, C, D of integer values, compute how many tuples (i ...

  2. 【LeetCode】18、四数之和

    题目等级:4Sum(Medium) 题目描述: Given an array nums of n integers and an integer target, are there elements ...

  3. [LeetCode] 454. 4Sum II 四数之和II

    Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such t ...

  4. Leetcode(18)-四数之和

    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满 ...

  5. 【LeetCode】18. 4Sum 四数之和

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:four sum, 4sum, 四数之和,题解,leet ...

  6. LeetCode 18. 4Sum (四数之和)

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...

  7. [LeetCode] 454. 4Sum II 四数之和之二

    Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such t ...

  8. LeetCode:两数之和、三数之和、四数之和

    LeetCode:两数之和.三数之和.四数之和 多数之和问题,利用哈希集合减少时间复杂度以及多指针收缩窗口的巧妙解法 No.1 两数之和 给定一个整数数组 nums 和一个目标值 target,请你在 ...

  9. LeetCode:四数之和【18】

    LeetCode:四数之和[18] 题目描述 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c ...

  10. 【LeetCode】18.四数之和

    题目描述 18. 四数之和 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 t ...

随机推荐

  1. 23种设计模式之自定义Spring框架(五)

    7,自定义Spring框架 7.1 spring使用回顾 自定义spring框架前,先回顾一下spring框架的使用,从而分析spring的核心,并对核心功能进行模拟. 数据访问层.定义UserDao ...

  2. 【安装文档】TRex流量分析仪保姆级安装指南--基于VMware虚拟机(ubantu18.04@Intel 82545EM)

    前言 既然你已经知道TRex并尝试搜索它的安装教程,这意味着你有一定的基础知识(至少知道自己需要什么).因此本文对于TRex的介绍部分会偏少 本次主要为TRex安装过程的一次记录(版本为v3.0.0) ...

  3. 微信小程序经纬度转化为具体位置(逆地址解析)

    小程序wx.getLocation只能获取经纬度, 这时候想要具体地址就需要借助第三方sdk(逆地址解析) 我这边第三方以腾讯位置服务举例 一. 首先小程序需要申请wx.getLocation接口权限 ...

  4. Java安全之Mojarra JSF反序列化

    Java安全之Mojarra JSF反序列化 About JSF JavaServer Faces,新一代的Java Web应用技术标准,吸收了很多Java Servlet以及其他的Web应用框架的特 ...

  5. ui自动化测试数据复原遇到的坑——2、python连接informix时pytest报致命错误Windows fatal exception: access violation

    python连接informix只能通过jdbc(需要先部署java环境.我试过到IBM上下载ODBC但结局是失败的),在执行pytest时发现有一串报错(大致是下面的这样): Windows fat ...

  6. 阿里云 ACK 接入观测云

    简介 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理.2021 年成为国内唯一连续三年入选 Gartner 公共云容器报告的 ...

  7. Go语言核心36讲26

    你好,我是郝林.今天我分享的主题是测试的基本规则和流程的(下)篇. Go语言是一门很重视程序测试的编程语言,所以在上一篇中,我与你再三强调了程序测试的重要性,同时,也介绍了关于go test命令的基本 ...

  8. php 程序员进化之路

    1.目标明确 2.消除干扰 3.自我激励 鸟哥 --2018年11月17日php年会

  9. 关于 python 中比较运算的顺序

    先来看一个例子: "x" > "y" == True 结果为 False 因为这里的表达式可以等同于 "x" > "y ...

  10. 【SQL】DML语句-SQL更新与删除:单表更新删除与连表更新删除语法

    〇.概述 1.资料 sql连表删除:http://www.lanxinbase.com/?tag=sql%E8%BF%9E%E8%A1%A8%E5%88%A0%E9%99%A4 sql实现两表关联更新 ...