代码算法训练营day7 | 454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

day7: 剩下的两题:

  • 15. 三数之和
  • 18. 四数之和

15. 三数之和

题目链接
状态:
文档:programmercarl.com

注意:
这和第一题中的四数相加Ⅱ很像,如果用哈希算法的思路就是:
两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 遍历数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。(i ≠ j ≠ k)

(哈希算法)思路:
首先创建一个二维数组,来存放符合条件的三元组。

因为要去重,不能出现像是:[-1,0,1] [0,1,-1]的情况,这样也算是重复。
所以要用一个 全局函数sort 进行排序,按照从小到大的顺序,就不会出现像上述一样的情况。
但是还要考虑一种去重:数组中有多个重复的元素。
这种去重就要通过剪枝来进行操作了。

代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[j], c = -(a + b)
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            //短路条件
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
            //不可以让 nums[i] == nums[i+1] --> 这样会让后面的j=i+1 失去遍历一些值的机会
                continue;
            }
            unordered_set<int> set; //set有自动去重的效果
            //开始遍历第二个数
            for (int j = i + 1; j < nums.size(); j++) { 
                if (j > i + 2
                        && nums[j] == nums[j-1]
                        && nums[j-1] == nums[j-2]) { // 三元组元素b去重
                    continue;
                }
                int c = 0 - (nums[i] + nums[j]);
                if (set.find(c) != set.end()) {
                    result.push_back({nums[i], nums[j], c});
                    set.erase(c);// 三元组元素c去重
                } else {
                    set.insert(nums[j]);
                }
            }
        }
        return result;
    }
};

注意:
用哈希法的话去重会很麻烦,让人一下子想不到,所以选择用双指针的方式,来不断的寻找合适的值。

(双指针)思路:
思路图
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //创建一个二维数组reslt 存放最后结果
        vector<vector<int>> result;
        //排序
        sort(nums.begin(),nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for(int i = 0;i<nums.size();i++)
        {
            //排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if(nums[i] > 0)
            {
                return result;
            }
            //对a进行去重
            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
                {
                    //结果==0,放入result中
                    result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    //进行b去重
                    while(right > left && nums[left] == nums[left+1])
                    {
                        left++;
                    }
                    //进行c去重
                    while(right > left && nums[right] == nums[right-1])
                    {
                        right--;
                    }

                    //找到答案后,双指针收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
       
    }
};

18. 四数之和

题目链接
状态:有一半思路,没做出来
文档:programmercarl.com

思路:
和上一题有些类似,都是使用双指针的方法,先找前两个数,在用双指针去遍历后两个数。

首先创建一个二维数组,存放最后的结果。
再对数组进行一个 全局函数sort 排序操作,进行第一步去重。

先找第一个数 k,先剪枝(一级剪枝:对a),也就是判断短路条件:都得是正数才行。
再对 k 进行去重操作(一级去重,对a)

然后再去找第二个数 i,i=k+1,先剪枝(二级剪枝:对b),和一级剪枝一样,都得是正数才行。
再对 i 进行去重操作(二级去重,对b)

有了前两个数的和了,开始创建双指针,一个是left = i+1,一个是right = nums.size()-1。
开始while循环遍历,此时的思路和三数之和这道题的思路是一样的:
四数之和>tar ==>> 数加多了,右指针左移。
四数之和<tar ==>> 数加少了,左指针右移。
四数之和=tar ==>> 存入二维数组中,此时要对left 和 right 进行去重操作,去重完毕之后,双指针收缩,继续下一轮。

最后返回二维数组。

代码:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        //二维数组,存放结果
        vector<vector<int>> result;
        //对数组进行排序
        sort(nums.begin(),nums.end());
        //四个数 k i left right
        for(int k = 0;k<nums.size();k++)
        {
            //一级剪枝:
            //首先对k进行剪枝:有不符合的情况直接就return
            //[-4 -1 0 5] tar=-5,so [-4 -1 0]就符合条件 但是-4 > target 
            //如果只判断这个条件的话 就会把正确的结果也剪掉了
            //nums[k] > target && target > 0 && nums[k] > 0
            //nums[k] > target && nums[k] >= 0
            //都是正数才行 才能满足这个短路条件
            if(nums[k] > target && target > 0 && nums[k] > 0 )
            {
                break;
            }
            
            //一级去重
            //k去重 要判断k>0,因为有一个k-1的操作
            if(k>0 && nums[k] == nums[k-1])
            {
                continue;
            }
            //遍历第二个数 i
            for(int i = k+1;i<nums.size();i++)
            {
                //二级剪枝
                //都得是正数才行
                //nums[k]+nums[i] > target && target > 0 && nums[k] + nums[i] > 0
                //nums[k]+nums[i] > target && nums[k] + nums[i] >= 0
                if(nums[k]+nums[i] > target && target > 0 && nums[k] + nums[i] > 0)
                {
                    break;
                }
                //二级去重
                if(i>k+1 && nums[i] == nums[i-1])
                {
                    continue;
                }
                //开始设置left right
                int left = i+1;
                int right = nums.size()-1;
                while(right > left)
                {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if((long)nums[k] + nums[i] + nums[left] +nums[right] > target)
                    {
                        //说明加多了
                        right--;
                    }
                    else if((long)nums[k] + nums[i] + nums[left] +nums[right] < target)
                    {
                        //说明加少了
                        left++;
                    }
                    else{
                        //==target
                        result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
                        //对left去重
                        while(right > left && nums[left] == nums[left+1])
                        {
                            left++;
                        }
                        //对right去重
                        while(right > left && nums[right] == nums[right-1])
                        {
                            right--;
                        }

                        //找到答案时,left、right指针同时收缩
                        right--;
                        left++;

                    }
                }
            }
        }
        return result;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++面向对象程序设计 - 创建学生类

在20世纪80年代提出了面向对象的程序设计&#xff08;Object oriented programming, OOP&#xff09;思想&#xff0c;在此形势下&#xff0c;C由AT&TBell&#xff08;贝尔&#xff09;实验室于20世纪80年代初在C语言的基础上开发成功&#xff0c;C保留了C语言原有的所有优…

(C语言)整数在内存中的存储与大小端

1. 整数在内存中的存储 整数的2进制表示方法有三种 &#xff0c;即 原码、反码和补码 有符号类型数据三种表示方法均有符号位和数值位两部分 &#xff0c;符号位都是用0表示“正” &#xff0c;用1表示“负” &#xff0c;最高位的一位是被当做符号位 &#xff0c;剩余的都是…

智慧公厕建设的主要目标是什么?

随着城市化进程的不断推进&#xff0c;公共厕所作为城市基础设施的重要组成部分&#xff0c;也变得越来越重要。为了提升公共厕所的管理水平、提供更好的服务质量&#xff0c;智慧公厕应运而生。智慧公厕的建设旨在通过信息化手段实现公共厕所的全面感知监测&#xff0c;实现公…

VGG论文学习笔记

题目&#xff1a;VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION 论文下载地址&#xff1a;VGG论文 摘要 目的&#xff1a;研究深度对精度的影响 方法&#xff1a;使用3*3滤波器不断增加深度&#xff0c;16和19效果显著 成绩&#xff1a;在ImageNet 20…

C++ 智能指针的使用

智能指针类型 在C程序中&#xff0c;普通变量使用栈内存&#xff0c;为函数运行时专用&#xff0c;结束后会自动释放&#xff0c;无须考虑内存释放问题。 但堆内存是共用的&#xff0c;其使用是通过指针变量的new来分配&#xff0c;使用delete来释放&#xff0c;因指针使用方便…

AI预测-一文解析AI预测数据工程

AI预测相关目录 AI预测流程&#xff0c;包括ETL、算法策略、算法模型、模型评估、可视化等相关内容 最好有基础的python算法预测经验 EEMD策略及踩坑VMD-CNN-LSTM时序预测对双向LSTM等模型添加自注意力机制K折叠交叉验证optuna超参数优化框架多任务学习-模型融合策略Transform…

Flink程序员开发利器本地化WebUI生成

前言 在flink程序开发或者调试过程中&#xff0c;每次部署到集群上都需要不断打包部署&#xff0c;其实是比较麻烦的事情&#xff0c;其实flink一直就提供了一种比较好的方式使得开发同学不用部署就可以观察到flink执行情况。 上代码 第一步&#xff1a;开发之前需要引入在本…

中间件漏洞(redis)

目录 1.Redis服务器被挖矿案例 2.redis常见用途 3.redis环境配置 4.redis的持久化机制 5.redis动态修改配置 6.webshell提权案例 7.定时任务bash反弹连接提权案例 8.SSH Key提权案例 9.redis安全加固分析 1.Redis服务器被挖矿案例 我没有体验过&#xff0c;那就看看别…

Flutter:构建美观应用的跨平台方案

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Fitten Code】“吊打“Github Copilot的国内免费代码辅助插件

&#x1f33b;个人主页&#xff1a;相洋同学 &#x1f947;学习在于行动、总结和坚持&#xff0c;共勉&#xff01; 目录 1.Github Copilot 2.Fitten Code 2.1 对话体验&#xff1a; 2.2 代码补全体验&#xff1a; 2.3 Pycharm安装方法&#xff1a; 2.4 Vscode安装方法…

git基础命令(一)

目录 基础概念git statusgit addgit diffgit loggit commit文件可以处于以下三种状态之一远程存储库与本地存储库参考 用于知识记录。后续有新的的内容&#xff0c;例子&#xff0c;将持续更新本文档。 基础概念 工作树&#xff1a;git add 之前&#xff0c;变动内容的文件列表…

Linux课程_____用户的管理

一、规则 用户至少属于一个组,在创建时如果不指定组,将会创建同名的组 用户只能有一个基本组(主组),但可以隶属于多个附加组 如果一个组作为某用户的基本组,此组将不能被删除 UID: 用户标识 GID: 组的标识 root管理员的uid及gid 都为0 二、用户的配置文件 1./etc/passwd …

<c语言学习> 整数和浮点数的存储方式

1.整数 有符号整数 第一位为符号位 1代表负数 0代表正数 举例&#xff1a; signed char 8 ---------------------> 0000 1000 -8 ----------------------> 1111 1000 &#xff08;补码形式存储&#xff09; 补码存储&#xff08;计算&#xff09;的妙处&…

Discourse 分类图片

我们可以在 Discourse 上为分类添加图片。 进入分类编辑界面&#xff0c;然后选择 Image 标签。 在 Images 标签下&#xff0c;上传分类需要的图片。 图片大小 图片的大小是 Discourse 进行控制的&#xff0c;高度为 150 PX 像素。 如果上传的图片大于 150 px 的高度像素&…

【JavaSE】类与对象

前言 Java是一门纯面向对象的语言&#xff0c;在面向对象的世界里&#xff0c;一切都为对象。它是解决问题的一种思想&#xff0c;主要依靠对象之间的交互完成一件事情。类与对象是我们学习面向对象最基础的知识&#xff0c;是面向对象实现的基石&#xff0c;可见它是有多么重…

打破数据孤岛,TDengine 与 Tapdata 实现兼容性互认证

当前&#xff0c;传统行业正面临着数字化升级的紧迫需求&#xff0c;但海量时序数据的处理以及数据孤岛问题却日益突出。越来越多的传统企业选择引入时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;升级数据架构&#xff0c;同时&#xff0c;为了克服…

cesium 动态立体墙效果

cesium 动态立体墙效果 以下为源码直接复制可用 实现效果 实现思路 通过修改“material”自定义材质实现动态效果 核心类(WallImageTrailMaterialProperty)自定义材质 class WallImageTrailMaterialProperty {constructor(options) {this

推荐一款好用的前端分页插件jqPaginator

jqPaginator 简洁、高度自定义的jQuery分页组件&#xff0c;适用于多种应用场景。 现在网上各种各样的分页组件很多&#xff0c;但是很难找到十分“称心如意”的&#xff0c;于是jqPaginator诞生了。 我心中理想的分页组件&#xff0c;要不受CSS框架限制&#xff0c;可以使用…

汽车电子零部件(6):DMS/OMS、CMS

前言: 有一个部件过去不曾有,而如今有可能要标准化标配化,那就是Driver Monitoring System (DMS)驾驶员监控系统、Occupant Monitoring System (OMS)乘客监控系统和Camera Monitor System(CMS)摄像头监控系统。 汽车视觉技术的创新推动先进驾驶辅助系统的变革(ADAS),并…

力扣39. 组合总和

Problem: 39. 组合总和 文章目录 题目描述思路及解题方法复杂度Code 题目描述 思路及解题方法 该问题是组合问题的一个变体&#xff0c;可以归纳为元素无重复可复选问题&#xff0c;其代码的实现几乎和组合问题一模一样&#xff0c;由于在组合问题中我们只需要利用一个变量在递…
最新文章