【leetcode 力扣刷题】 两数/三数/四数之和 哈希表和双指针解题

news2024/11/16 19:34:26

两数/三数/四数之和 题目合集

  • 哈希表求解
    • 1. 两数之和
    • 454. 四数相加Ⅱ
  • 双指针求解
    • 15.三数之和
    • 18. 四数之和

这个博客是关于:找出数组中几个元素,使其之和等于题意给出的target 这一类题目的,但是各个题之间又有些差异,使得需要用不同的方法求解。 可以分为两类:一类是哈希表,一类是双指针。接下来详细讲解。

哈希表求解

首先,还是要明确,哈希表的主要用途是查找某个元素是否存在

1. 两数之和

题目链接:1. 两数之和
题目内容:
在这里插入图片描述
根据题意就是在数组中找到两个元素使其和为target,最终返回的是下标。 我20年做这题的时候只会暴力求解,两层循环遍历所有可能的nums[i]+nums[j],看题解的哈希表云里雾里。 这次在哈希表的题目列表里看到这道题很震惊hhhhhh,竟然能看懂题解了呢……欣慰(捂脸哭)。
分析两层循环的目的:第一层循环遍历nums[i],第二次循环寻找nums[j] ,nums[i] + nums[j] =target,换个思路实际上是在下标i+1~n-1这个范围内查找是否存在target - nums[i],所以?可以考虑用哈希表来高效的查找。
这里再转换一下,实际上先初始化 j = 1,然后在 0~j-1这个范围内查找是否存在target - nums[j]实现起来更容易。因为随着j的增加,0~j-1这个范围越来越大,构造的哈希表越来越大,比较合理。
用什么实现哈希表呢? 题意要求返回下标,如果直接用unordered_set只能存数组元素值value,不能同时存下标index,因此用unordered_map,数组元素作key,下标作value(因为按照元素值来查找的)。代码实现如下(C++):

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> my_map; //map用来存没有匹配的元素及其下标
        //遍历nums
        for(int i = 0; i < nums.size(); i++){
        	//如果0~i-1中存在元素与nums[i]之和满足要求,直接返回
            if(my_map.count(target - nums[i])){
            	//map[key]里面存的就是对应的下标
                return {my_map[target - nums[i]], i};
            }
            else{ 
            //如果0~i-1中没有需要的元素,将nums[i]添加到map中
            //因为当前的nums[i']可能与后面(i向后移动后)nums[i]相加满足条件
                my_map[nums[i]] = i;  
            }
        }
        return {};
    }
};

454. 四数相加Ⅱ

题目链接:454. 四数相加Ⅱ
题目内容:
在这里插入图片描述
根据题目内容的描述,以及观察示例1,实际上题目就是在四个数组中,分别找一个元素,使得和=0。本题也没有强调元素不能重复出现,比如最极端的例子:
在这里插入图片描述
所以!实际上按照暴力求解的思路,四层循环遍历四个数组,找到所有可能的nums1[i] + nums2[j] + nums3[k] + nums4[p] ==0的(i,j,k,p)就好了【最终只需要返回次数】。但是暴力求解时间复杂度O(n^4)。怎么降低呢? x+y+z+m=0,那么任意选两个数之和,与剩下两个数的和互为相反数的,x+z= -(y+m)。因此可以考虑:

  • 先遍历nums1和nums2得到所有可以的sum=nums1[i]+nums2[j],并以sum值为key,sum出现的次数为value,存为哈希表(unordered_map实现);
  • 再遍历nums3和nums4,查找map中是否存在 -(nums3[k]+nums4[p]),如果存在就找到了满足条件的四元组,并且map[-(nums3[k]+nums4[p])]等于几,就找到了几个这样的四元组;
    代码实现(C++):
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
    	//key是sum=nums1[i]+nums2[j] value是能相加得到这个sum的次数
        unordered_map<int,int> sum;
        int ans = 0;
        //先遍历nums1和nums2
        for(int i = 0; i < nums1.size(); i++)
            for(int j = 0; j < nums2.size(); j++){
                sum[nums1[i] + nums2[j]]++;
            }
        //再遍历nums3和nums4
        for(int i = 0; i < nums3.size(); i++)
            for(int j = 0; j < nums4.size(); j++){
            	//查找是否有满足条件的四元组
                if(sum.count(0 - nums3[i] - nums4[j]))
                    ans+= sum[0 - nums3[i] - nums4[j]];
            }
        return ans;
    }
};

注意,nums1、nums2、nums3和nums4可以随便组合,上面代码是先遍历nums1和nums2,实际上可以先遍历nums2+nums4等等等,随便选两个数组先遍历,再遍历剩下两个数组。

双指针求解

接下来的题目,解题思路是先排序,再利用双指针。 重点在于,返回的满足条件的元组不能重复。这里的不能重复就面临着需要去重

15.三数之和

题目链接:15. 三数之和
题目内容:在这里插入图片描述
题目的重点在于返回的三元组不能重复。示例①中出现了[-1,0,1]和[0,1,-1】这样的就算重复,那么可以想到,如果先排序,去重很方便,只需要判断nums[i]和nums[i-1]的关系(相等和不相等)。【能够排序是因为最终返回的是nums[i]组成的三元组,而不是对应的下标;如果是下标的话,排序后元素下标就变化了,比如上面的两数之和就不能先排序,因为它要求返回下标。】
排序后,遍历nums元素,用i控制,同时使用left和right两个双指针,过程如下:

  • left = i + 1,right = nums.size() -1;因为已经先排序了,如果nums[i] + nums[left] + nums[right] < 0,那就移动left(left++),向后寻找更大的数;
  • 如果nums[i] + nums[left] + nums[right] > 0,那就移动right (right- -),向前寻找更小的数;
  • 如果nums[i] + nums[left] + nums[right] = 0,就找到了这样的三元组,向结果数组中添加;
    存在的问题
  • 如果nums[i]已经大于0了,left和right都在nums后面,是≥nums[i]的,三个元素之和只会更大,后续是找不到nums[i] + nums[left] + nums[right] = 0的,因此当nums[i]>0时,不再往后遍历nums的元素;
  • 如何去重呢?当nums[i]等于nums[i-1]时,后续找到的满足条件的nums[left]和nums[right],也必然和nums[i-1]那一轮找到的结果重合。因为不能重复,因此时没有必要的。
    代码实现(C++):
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        sort(nums.begin(), nums.end()); //先排序
        for(int i = 0; i < nums.size() - 2 ; i++){ //最外层 用i控制 遍历nums
            if(nums[i] > 0) break; //如果nums[i]>0,后续不可能找到结果,直接跳出循环
            //三元组第一个元素的去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            //初始化双指针            
            int left = i + 1, right = nums.size() - 1;
            while(left < right){
            	//满足条件,向结果数组中添加元素
                if(nums[i] + nums[left] + nums[right] == 0){
                    ans.emplace_back(vector<int>{nums[i], nums[left], nums[right]});
                    //三元组第二个元素去重,跳到不等于当前元素的下标处
                    do{
                        left++;
                    }while(left < right && nums[left] == nums[left - 1]);
					//三元组第三个元素去重,跳到不等于当前元素的下标处
                    do{
                        right--;
                    }while(left < right && nums[right] == nums[right + 1]);
                }
                else{
                	//更小时移动left
                    if(nums[i] + nums[left] + nums[right] < 0){
                    	//如果当前元素不满足,和当前元素重复的那些值可以直接跳过
                        do{
                            left++;
                        }while(left < right && nums[left] == nums[left - 1]);
                    }
                    else {
                    //更大时移动right,并跳过和当前元素相等的元素
                        do{
                            right--;
                        }while(left < right && nums[right] == nums[right + 1]);   
                    }
                }
            }
        }
        return ans; 
    }
};

时间复杂度从三层循环的O(n^3)变成了一层遍历+一层双指针O(n^2)。

18. 四数之和

题目链接:14. 四数之和
题目内容:
在这里插入图片描述
这题目说得越来越看不懂,但实际上,就是从上一题三数之和变成了找四个数之后;固定的等于0,变成了等于target。同样时要求不重复,并且返回元素值组成的数组而不是下标。 因此套用三数之和的解题思路,先排序+双指针。不同点:

  • 外层需要两层循环,里面一层用双指针遍历。
  • 之前nums[i] > 0,就能跳出循环。但是本题nums[i] > target不能跳出循环,因为如果nums[i]是-4,target是-6,但是nums[i+1]=-2,这样后续也是可能找到四个元素之和等于-6的。因此只有在target>=0的情况下,nums[i] > target了,已经排序了那么nums[i]之后元素都大于0,相加会越来越大,找不到相加等于target的。
    代码实现(C++):
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> ans;
        sort(nums.begin(), nums.end());//先排序
        if(nums.size() < 4) return {}; //题意中n>=1,如果长度小于4直接返回空
        //最外层循环
        for(int i = 0; i <nums.size() - 3; i++){
        	//提前结束 注意target>=0
            if(nums[i] > target && target >=0 ){
                break;
            }
            //一级去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            //第二层循环 j控制
            for(int j = i + 1; j < nums.size() - 2; j++){
            	//第一层没有跳出,但是第二层可加上nums[j]后可能跳出,同样注意需要target>=0才行
                if(nums[i] + nums[j] > target && target >= 0)
                    break;
                //二级去重
                if(j > i + 1 && nums[j] == nums[j-1])
                    continue;
                //双指针循环
                int left = j + 1, right = nums.size() -1;
                while(left < right){
                	//找到满足条件的……操作同三数之和
                	//注意题目中nums[i]是在[-10^9,10^9],这里不换成long,有测试样例不通过……真的离谱
                    if(long(nums[i]) + long(nums[j]) + long(nums[left]) + long(nums[right]) == long(target)){
                        ans.emplace_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
                        do{
                            left++;
                        }while(left < right && nums[left] == nums[left - 1]);//跳过相同元素,第三个元素的去重
                        do{
                            right--;
                        }while(left < right && nums[right] == nums[right + 1]);//跳过相同元素,第四个元素的去重
                    }
                    else{
                    	//如果大了移动right
                        if(long(nums[i]) + long(nums[j]) + long(nums[left]) + long(nums[right]) > long(target)){
                            do{
                                right--;
                            }while(left < right && nums[right] == nums[right + 1]);
                        }
                        else{//如果小了移动left
                           do{
                               left++;
                           } while(left < right && nums[left] == nums[left - 1]);
                        }
                    }
                }
            }
        }
    return ans;
    }
};

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

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

相关文章

lwIP更新记10:IP 冲突检测

lwip-2.2.0-rc1 版本于 2023 年 6 月 29 日发布&#xff0c;带来了我期盼已久的 IPv4 冲突检测 功能。 lwip-2.2.0-rc1 版本重新回归了 master 分支&#xff08;主分支&#xff09;&#xff0c;不再使用单独的稳定分支。 master 分支 是一个 Git&#xff08;版本控制程序&…

C#8.0本质论第四章--操作符和控制流程

C#8.0本质论第四章–操作符和控制流程 4.1操作符 有些操作符以符号的形式出现&#xff0c;例如、-、?.或者??等&#xff0c;而另一些操作符则为关键词&#xff0c;例如default和is。 4.1.1一元正负操作符 一元正操作符()对值几乎没有影响&#xff0c;它在C#中是多余的。…

python 连接Redis 数据库

pip install redis python代码 import redis# 连接数据库 r redis.Redis(host192.168.56.15, port6379, db0)# 存储数据 #r.set(key, value) r.set(name, zaraNet)# 获取数据 value r.get(name) print(value)# 关闭连接&#xff08;可选&#xff09; r.close()

AP9196 DC-DC 升压 升降压 恒流电源管理芯 LED电源驱动IC

产品说明 AP9196 是一系列外围电路简洁的宽调光比升压调光恒流驱动器&#xff0c;适用于 3-40V 输入电压范围的 LED照明领域。AP9196 采用我司专利算法&#xff0c;可以实现高精度的恒流效果&#xff0c;输出电流恒流精度≤3&#xff05;&#xff0c;电压工作范围为5-40V&…

Redis 缓存满了怎么办?

引言 Redis 缓存使用内存来保存数据&#xff0c;随着需要缓存的数据量越来越大&#xff0c;有限的缓存空间不可避免地会被写满。此时&#xff0c;应该怎么办&#xff1f;本篇文章接下来就来聊聊缓存满了之后的数据淘汰机制。 值得注意的是&#xff0c;在 Redis 中 过期策略 和…

【rar密码】rar压缩包密码列表

之前给大家介绍过WinRAR自动加密的设置方法&#xff0c;今天再介绍一种RAR压缩包加密方法&#xff1a;整理密码。 什么是整理密码&#xff1a; 在加密rar文件的时候&#xff0c;点击下拉框选择密码&#xff0c;不用输入密码 设置方法&#xff1a; 前面的操作步骤和设置自动…

正中优配:尾盘拉升的股票第二天的走势?

尾盘拉升是指买卖日快结束时股票价格呈现上涨的状况。关于许多投资者来说&#xff0c;这一般是好事情&#xff0c;因为它可认为他们带来更高的收益。但是&#xff0c;人们常常会问尾盘拉升的股票第二天的走势怎么。本文将从多个角度进行剖析。 首要&#xff0c;咱们需求认识到这…

这所211太好考了!263分上岸!平均分300分!

一、学校及专业介绍 宁夏大学&#xff08;Ningxia University&#xff0c;简称&#xff1a;宁大&#xff09;&#xff0c;是中国教育部与宁夏回族自治区人民政府合建高校&#xff0c;位列国家“双一流”“211工程”重点建设高校&#xff0c;国家“中西部高校综合实力提升工程”…

cpolar+JuiceSSH实现手机端远程连接Linux服务器

文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …

成绩管理神器

各位老师们是不是需要一款方便快捷班级查询工具&#xff1f;其实易查分就可以帮助老师和学生轻松地管理和查询成绩。查分系统是现代教育管理的一项重要工具&#xff0c;传统纸质成绩单已被易查分电子成绩单所取代&#xff0c;带来了方便快捷等多种优势&#xff0c;为学生、家长…

scroll 和 wheel 事件的区别

listRef.addEventListener(“scroll”, onScroll)&#xff0c;onScroll 里面打 log 打不出来。 我觉得 list 是一个长的列表&#xff0c;比 container 要长&#xff0c;应该能滚动才是&#xff0c;不知道为啥滚动不了 当时我想到的是可能电视不支持这个事件&#xff0c;但是问…

OSCS开源安全周报第 56 期:Apache Airflow Spark Provider 任意文件读取漏洞

本周安全态势综述 OSCS 社区共收录安全漏洞 3 个&#xff0c;公开漏洞值得关注的是 Apache NiFi 连接 URL 验证绕过漏洞(CVE-2023-40037)、PowerJob 未授权访问漏洞(CVE-2023-36106)、Apache Airflow Spark Provider 任意文件读取漏洞(CVE-2023-40272)。 针对 NPM 、PyPI 仓库…

饼图的legend文字太长和数量太多处理

legend文字太长和数量太多 在我们使用图表饼图的时候会发现因为数据太多导致页面的布局发现重叠或者不好看&#xff0c;比如label的文字太长了&#xff0c;legend的数量太多了等一些问题&#xff0c;所以今天我们就来聊聊遇到这些问题的是可以通过那些设置来进行改进 文字太长…

第12步---MySQL的JDBC操作

第12步---MySQL的JDBC操作 1.概述 采用Java API 的方式实现数据之间的操作。 根据不同的数据库采用了不同的驱动&#xff0c;接口是一致的。 下载的地址 MySQL :: Download MySQL Connector/J (Archived Versions) 2.执行流程 注册驱动 创建连接 执行sql语句的对象 结果…

10个最受欢迎的免费STL模型下载网站【2023】

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 你是否决定立即购买 3D 打印机&#xff1f; 或者可能一直在考虑并正在寻找更多细节来了解它如何使您受益&#xff1f; 好吧&#xff0c;总有一天&#xff0c;你拥有 3D 打印活动所需的所有知识和资源&#xff0c;但没有时…

【SpringCloud】SpringCloudAlibaba官网资料

出现原因 Spring Cloud Netflix Projects Entering Maintenance Mode 官网 博客 https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md官网 https://spring.io/projects/spring-cloud-alibaba#overview英文 https://github.com/alibaba/spring-cloud-…

refresh大揽

注意在每一步大操作之前都有一个前期准备 prepareRefresh&#xff08;&#xff09; 设置spring启动的时间 设置spring关闭和开启的标志位 获取环境对象&#xff0c;并设置一些属性值&#xff0c;是系统环境的不是xml设置的 设置监听器&#xff0c;以及需要发布事件的集合。 Con…

Java算法_ BST 中第 k 个最小元素 (LeetCode_Hot100)

题目描述&#xff1a;给定一个二叉搜索树的根节点 &#xff0c;和一个整数 &#xff0c;请你设计一个算法查找其中第 个最小元素&#xff08;从 1 开始计数&#xff09;。 获得更多&#xff1f;算法思路:代码文档&#xff0c;算法解析的私得。 运行效果 完整代码 /*** 2 * Aut…

实验三 HBase1.2.6安装及配置

系列文章目录 文章目录 系列文章目录前言一、HBase1.2.6的安装二、HBase1.2.6的配置2.1 单机模式配置2.2 伪分布式模式配置 总结参考 前言 在安装HBase1.2.6之前&#xff0c;需要安装好hadoop2.7.6。 本篇文章参考&#xff1a;HBase2.2.2安装和编程实践指南 一、HBase1.2.6的安…

双频RTK定位技术原理及解决方案

双频RTK定位技术 双频RTK&#xff08;Real-Time Kinematic&#xff09;定位技术是一种利用卫星导航系统进行高精度实时定位的方法&#xff0c;它通过同时使用两个不同频率的载波信号来测量载波相位差&#xff0c;从而提高定位精度和抗干扰能力。以下是双频RTK定位技术的原理和解…