四数之和(详细题解:双指针+排序)

news2024/11/29 10:31:02

18. 四数之和

难度中等1502

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • 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
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

解题思路:排序+双指针

这道题其实就是 15. 三数之和 的拓展,本质上还是一样的,只不过剪枝和去重的细节又多了一点!

​ 首先一开始还是排序,这个问题我们已经在三数之和那道题中说过了,具体的可以去翻阅笔记查看!

​ 接着就是遍历 nums,因为这次是四个数,双指针分别占了两个,所以我们在最外层的循环还需要套两层,比三数之和多了一层循环,最重要的是多了一层循环,也就是多了一个剪枝和去重工作!但是大体步骤和三数之和是类似的!

1、先来讲讲最外层这个 i 的剪枝和去重工作:

​ 这道题三数之和不太一样,这道题要求四个数的和不能超过 target,那么我们在最外层的剪枝判断中能不能说直接 nums[i] > target 就剪枝呢?肯定是不行的,因为 target 可能是负数,并且 nums[i] 以及 nums[i] 后面的数也可能是负数,这个时候一个数加上负数,它们的和反而是会变小的,所以 不能直接判断是否 nums[i] > target 就剪枝!举个例子,比如 {-4, -1, 0, 0} 数组中我们要找的是和为 -5 的,那么这个数组刚刚好就是一个解,但是如果我们如果直接判断 nums[i] > target 也就是 -4 > -5 后剪枝,那么就错过了这个解,那么就错了!

​ 所以最外层的剪枝工作是需要修改一下的,我们还需要判断 nums[i] 是否大于等于0,因为我们的数组是排序过的,这样子就能避免 nums[i] 后面加的数是负数,所以正确的剪枝判断是: if(nums[i] > target && nums[i] >= 0) break;

​ 接下来就是最外层的去重操作,这个和三数之和是一模一样的,只要判断 nums[i]nums[i - 1] 是否相同,是的话就说明重复了,直接 continue,除此之外 还需要注意边界问题就是需要让 i > 0 防止 nums[i -1] 索引越界!具体的原因可以参考三数之和的笔记!

2、接下来讲一下第二层 j 的剪枝和去重工作:

​ 首先 j 肯定是通过最外层的 i + 1 来开始遍历的,所以对于去重工作,都是类似的,只是这里不是 nums[j] > 0而是 nums[j] > i + 1 来进行防止索引越界,并且只要 nums[j] == nums[j - 1] 我们就直接 continue 即可!

​ 对于第二层的剪枝,不只是单单的判断 nums[j] > target 就剪枝,因为有可能我们最外层的 nums[i] 就是一个负数,而 nums[i] + nums[j] 之后得到的数比 target 还小,那么这就不能剪枝了,所以我们要考虑到最外层的情况!

正确的剪枝操作:if(nums[i] + nums[j] > target && nums[i] >= 0 && nums[j] >= 0) continue;

​ 上述剪枝操作也可以进行一下化简:if(nums[i] + nums[j] > target && nums[i] >= 0) continue;

​ 这个化简就是简单的数学问题了,要求 nums[i] >= 0,因为数组的有序的,所以 nums[j] 肯定也是 >= 0 的,所以就没必要判断 j了!

3、最后说一下双指针的移动

​ 这个大概操作和三数之和是一样的!但是有一些细节问题,就是直接是三个数与 target 比较,现在要变成四个数!

​ 除此之外,我们四个数相加,对于这道题的数据来说是会发生溢出的,所以我们在相加的时候需要将它们的类型转化为 long 类型以上才行,而对于类型转化表达式,我们只需要对其中一个变量进行类型转化,其它的三个变量会根据不同类型进行隐式类型转化的,也就是一个类型是 long,其它类型是 int 的话,最后相加的结果还是 long!这样子就不会溢出了!

​ 其它问题就和三数之和是一样的!

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end()); // 别忘了排序!

        vector<vector<int>> vv;
        int n = nums.size();
        for(int i = 0; i < nums.size(); ++i)
        {
            // 进行一级剪枝和一级去重
            // 剪枝中注意如果target是负数的话且nums[i]>target,那么直接不能剪枝
            // 因为虽然nums[i]>target,但是nums[i]加上nums[j]不一定比target大,所以要排除nums[i]小于0的情况!
            if(nums[i] > target && nums[i] >= 0)
                break;
            if(i > 0 && nums[i] == nums[i - 1])
                continue;

            for(int j = i + 1; j < nums.size(); ++j)
            {
                // 进行二级剪枝和二级去重
                // 注意这里剪枝的时候不只是判断nums[j]>target,因为有可能nums[i]是负数,这样子它们两个数相加之后可能会比target小
                // 这里写成if(nums[i] + nums[j] > target && nums[i] >= 0) 也是可以的,数学问题!
                if(nums[i] + nums[j] > target && nums[i] + nums[j] >= 0)
                    break;

                // 去重时候如果前一个是i的话,那么相当于j是i这一轮的第一遍执行,所以不需要continue
                if(j > i + 1 && nums[j] == nums[j - 1])
                    continue;

                int left = j + 1, right = n - 1;
                while(left < right)
                {
                    // 四个数加起来可能会溢出,所以要先变成long类型
                    if((long) nums[i] + nums[j] + nums[left] + nums[right] > target)
                        right--;
                    else if((long) nums[i] + nums[j] + nums[left] + nums[right] < target)
                        left++;
                    else
                    {
                        vv.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});

                        // left和right的去重
                        while(left < right && nums[right] == nums[right - 1])
                            right--;
                        while(left < right && nums[left] == nums[left + 1])
                            left++;
                        
                        // 别忘了再更新一次left和right才是不重复的那个值
                        left++;
                        right--;
                    }
                }
            }
        }
        return vv;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 魔法类 Unsafe 详解

阅读过 JUC 源码的同学&#xff0c;一定会发现很多并发工具类都调用了一个叫做 Unsafe的类。 那这个类主要是用来干什么的呢&#xff1f;有什么使用场景呢&#xff1f;这篇文章就带你搞清楚&#xff01; Unsafe 介绍 Unsafe 是位于 sun.misc 包下的一个类&#xff0c;主要提…

要发计算机SCI论文,参考文献应该怎么引用? - 易智编译EaseEditing

SCI论文发表中经常被引用的参考文献主要有以下四种。 一是关于具体的实验的方法&#xff0c;二是支持性或者有冲突的证据&#xff0c;三是比较有用的类似的文献&#xff0c;四是有历史背景的和有意义的文献。 其实归根到底&#xff0c;你引用的SCI的参考文献必须对你的论文发表…

031_SSS_Imagic Text-Based Real Image Editing with Diffusion Models

Imagic: Text-Based Real Image Editing with Diffusion Models 1. Introduction 本文提出了一种新的基于Diffusion的方法称作Imagic&#xff0c;可以实现复杂的基于文本的图像编辑。与之前的方法不同&#xff0c;本文的方法只需要一张输入图像和一个目标文本&#xff0c;并且…

线程私有变量ThreadLocal详解

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 烈火试真金&#xff0c;逆境试强者。——塞内加 文章目录什么是ThreadLocalThreadLocal 原理set()方法get()方法remove()方法ThreadLocal 的Hash算法ThreadLocal 1.7和1.8的区别ThreadLocal 的问题ThreadLoca…

StarRocks获评「2022 中国开源社区健康案例」!

近日&#xff0c;OSCHINA 2022年度中国开源项目评选结果正式揭晓&#xff0c;StarRocks开源社区成功入选 OSCHINA “2022中国开源社区健康案例”&#xff01;开源社区健康指的是围绕一个开源项目形成的社区中关于项目的技术迭代、社区的组织架构、成员构成、开源治理、上下游协…

【手写 Vuex 源码】第十二篇 - Vuex 插件机制的实现

一&#xff0c;前言 上一篇&#xff0c;主要介绍了 Vuex 插件的开发&#xff0c;主要涉及以下几个点&#xff1a; Vuex 插件的使用介绍&#xff1b;Vuex 插件开发和使用分析&#xff1b;Vuex 插件机制的分析&#xff1b; 本篇&#xff0c;继续介绍 Vuex 插件机制的实现&…

moveToCoordinateF3DconcatenateRotations

moveToCoordinate 演示视频: 注意:前提是3~6轴机器人机构且不是PickAndPlace 该方法_3D。Poses.moveToCoordinate 移动由 指定的对象,该对象 对应于支持的机器人配置之一,只要标识的机器人配置支持,其第一个动画指向指定坐标和指定旋转。这无需您定义姿势即可工作。 工…

Python期末复习知识点大合集(期末不挂科版)

Python期末复习知识点大合集&#xff08;期末不挂科版&#xff09; 文章目录Python期末复习知识点大合集&#xff08;期末不挂科版&#xff09;一、输入及类型转换二、格式化输出&#xff1a;字符串的format方法三、流程控制四、随机数生成五、字符串六、序列索&#xff08;含字…

stm32f407探索者开发板(十五)——NVIC中断优先级管理

文章目录零、前言一、NVIC中断优先级分组1.1 中断的管理方法1.2 抢占优先级&相应优先级的区别1.3 举例1.4 特别说明1.5 中断优先级分组函数二、NVIC中断优先级设置2.1 中断设置相关寄存器2.2 中断设置优先级2.2.1 中断优先级控制的寄存器组 IP[240]2.2.2 中断使能寄存器组 …

TCP核心机制之连接管理详解(三次握手,四次挥手)

目录 前言&#xff1a; 建立连接 建立连接主要两个TCP状态&#xff1a; 断开连接 断开连接的两个重要状态 小结&#xff1a; 前言&#xff1a; TCP是如何建立对端连接&#xff0c;如何断开连接&#xff0c;这篇文章会详细介绍。 建立连接 首先明确连接的概念&#xff1a…

Docker系列之九巧用Nginx

前言 Nginx是个很棒的反向代理服务工具&#xff0c;之前我都是直接安装到服务器上去&#xff0c;过程繁琐也许不是难以接受&#xff0c;但是有更简单地Docker镜像服务&#xff0c;我自然没必要再次舍近求远。 一些配置是参考自我以前的文章Docker系列一安装Docker和Hexo历险记…

影片自由,丝滑流畅,Docker容器基于WebDav协议通过Alist挂载(百度网盘/阿里云盘)Python3.10接入

使用过NAS(Network Attached Storage)的朋友都知道&#xff0c;它可以通过局域网将本地硬盘转换为局域网内的“网盘”&#xff0c;简单理解就是搭建自己的“私有云”&#xff0c;但是硬件和网络成本都太高了&#xff0c;有点可望而不可及的意思。Alist开源库则可以满足我们&…

Docker+Nginx+KeepaLived实现Nginx一主一从高可用

系统版本 Centos7 IP&#xff1a;10.10.11.79 Master IP&#xff1a;10.10.11.81 Slave 虚拟ip&#xff1a;10.10.11.77 客户端发起一个请求 &#xff0c;请求没有到Nginx的实际IP上&#xff0c;而是请求的虚拟IP(会和实际IP通过配置文件进行绑定&#xff09; 如果有一台Nginx…

Linux进阶(Shell编程学习一)

由于shell脚本在java项目运维方面极其重要&#xff0c;比如服务的启动脚本&#xff0c;日志的分割脚本&#xff0c;文件的管理脚本大多都是shell脚本去实现的。所以作为java开发者懂linux的基本命令&#xff0c;会基本的shell编程是必要的。 Shell 是一个用 C 语言编写的程序&…

nodejs+vue学生考试成绩数据分析与可视化系统vscode

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.3 B/S结构 4 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;技术背景…

springboot 集成driud

druid官方文档导入jar包<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid-spring.version}</version></dependency>配置数据源spring:datasource:url: jdbc:m…

什么是销售管理?销售管理的五大职能

销售管理听起来很简单&#xff0c;似乎只是负责销售并确保客户满意&#xff0c;但事实上&#xff0c;它远不止于此。 销售管理的实际职能包括监督销售团队的工作&#xff0c;制定计划和设定目标&#xff0c;通常还包括确保销售流程的效率以获得最佳业务结果。 什么是销售管理…

千兆网口down/up偶发link成百兆问题

a. 软硬件环境&#xff1a;​NXP LS1043A IGB211&#xff08;pcie接口千兆网卡&#xff09;:linux 4.9.19 igb driver Nvidia Xavier NX RTL8211F: linux4.9​ ​NXP LS1043A -----(pcie)--- IGB211 <---(PCB走线代替网线)-----> RTL8211F&#xff08;phy&#xff09;…

剑指 Offer 56 - II. 数组中数字出现的次数 II

题目 在一个数组 nums 中除一个数字只出现一次之外&#xff0c;其他数字都出现了三次。请找出那个只出现一次的数字。 思路 这题是剑指 Offer 56 - I. 数组中数字出现的次数的变体&#xff0c;本题只有一个数num出现一次&#xff0c;其余的均出现三次 三次的话使用异或消无法…

云计算与 SaaS 有何区别?

云计算与 SaaS 有何区别&#xff1f;众所周知&#xff0c;SaaS是云计算的三种服务模式其中之一。 三种分别是&#xff1a; Iaas&#xff1a;基础设施即服务Paas&#xff1a;平台即服务SaaS&#xff1a;软件即服务 对于三者的区别&#xff0c;一起来吃顿烤肉&#xff0c;听我慢…