leetcode 力扣刷题 两数/三数/四数之和 哈希表和双指针解题
两数/三数/四数之和 题目合集
- 哈希表求解
- 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用来存没有匹配的元素及其下标//遍历numsfor(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和nums2for(int i = 0; i < nums1.size(); i++)for(int j = 0; j < nums2.size(); j++){sum[nums1[i] + nums2[j]]++;}//再遍历nums3和nums4for(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控制 遍历numsif(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{//更小时移动leftif(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>=0if(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{//如果大了移动rightif(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{//如果小了移动leftdo{left++;} while(left < right && nums[left] == nums[left - 1]);}}}}}return ans;}
};
相关文章:
leetcode 力扣刷题 两数/三数/四数之和 哈希表和双指针解题
两数/三数/四数之和 题目合集 哈希表求解1. 两数之和454. 四数相加Ⅱ 双指针求解15.三数之和18. 四数之和 这个博客是关于:找出数组中几个元素,使其之和等于题意给出的target 这一类题目的,但是各个题之间又有些差异,使得需要用不…...
(搜索) 剑指 Offer 12. 矩阵中的路径 ——【Leetcode每日一题】
❓剑指 Offer 12. 矩阵中的路径 难度:中等 给定一个 m * n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构…...
构建高可用的去中心化微服务集群架构指南
随着云计算、大数据和物联网的快速发展,企业对于可扩展的、高性能的微服务架构的需求也日益增长。传统的集中式架构已经不能满足这些需求,因此出现了去中心化的微服务集群架构。本文将介绍如何构建高可用的去中心化微服务集群架构,以满足企业…...
Sui主网升级至V1.7.1版本
Sui主网现已升级至V1.7.1版本,此升级包含了多项修复和优化。升级要点如下所示: #12915 协议版本提升至20版本。 在Sui框架中新增Kiosk Extensions API和一个新的sui::kiosk_extension模块。 您可以使用该API构建自定义的Kiosk应用程序,以…...
自然语言处理从入门到应用——LangChain:索引(Indexes)-[基础知识]
分类目录:《自然语言处理从入门到应用》总目录 索引(Indexes)是指为了使LLM与文档更好地进行交互而对其进行结构化的方式。在链中,索引最常用于“检索”步骤中,该步骤指的是根据用户的查询返回最相关的文档:…...
k8s集群监控方案--node-exporter+prometheus+grafana
目录 前置条件 一、下载yaml文件 二、部署yaml各个组件 2.1 node-exporter.yaml 2.2 Prometheus 2.3 grafana 2.4访问测试 三、grafana初始化 3.1加载数据源 3.2导入模板 四、helm方式部署 前置条件 安装好k8s集群(几个节点都可以,本人为了方便实验k8s集…...
nginx反向代理流程
一、nginx反向代理流程 反向代理:使用代理服务器来接受internet上的连接请求,然后将请求转发给内部网络中的上游服务器,并将上游服务器得到的结果返回给请求连接的客户端,代理服务器对外表现就是一个web服务器。Nginx就经常拿来做…...
Java“牵手”根据店铺ID获取淘宝店铺所有商品数据方法,淘宝API实现批量店铺商品数据抓取示例
淘宝天猫商城是一个网上购物平台,售卖各类商品,包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取淘宝整店所有商品详情页面评价内容数据,您可以通过开放平台的接口或者直接访问淘宝商城的网页来获取店铺所有商品详情信息内的评论数据…...
从0开始yolov8模型目标检测训练
从0开始yolov8模型目标检测训练 1 大环境 首先有大环境,即已经准备好了python、nvidia驱动、cuda、cudnn等。 2 yolov8的虚拟环境 2.1 创建虚拟环境 conda create -n yolov8 python3.102.2 激活虚拟环境 注意:激活虚拟环境的时候,需要清…...
设计模式-抽象工厂模式
抽象工厂模式:该模式是对工厂模式的拓展,因为工厂模式中创建的产品都需要继承自同一个父类或接口,创建的产品类型相同,无法创建其他类型产品,所以抽象工厂模式对其进行拓展,使其可以创建其他类型的产品。 …...
如何用Apipost实现sign签名?
我们平常对外的接口都会用到sign签名,对不同的用户提供不同的apikey ,这样可以提高接口请求的安全性,避免被人抓包后乱请求。 如何用Apipost实现sign签名? 可以在Apipost中通过预执行脚本调用内置的JS库去实现预执行脚本是在发送请求之前自…...
Hive底层数据存储格式
前言 在大数据领域,Hive是一种常用的数据仓库工具,用于管理和处理大规模数据集。Hive底层支持多种数据存储格式,这些格式对于数据存储、查询性能和压缩效率等方面有不同的优缺点。本文将介绍Hive底层的三种主要数据存储格式:文本文件格式、Parquet格式和ORC格式。 一、三…...
双向-->带头-->循环链表
目录 一、双向带头循环链表概述 1.什么是双向带头循环链表 2.双向带头循环链表的优势 3.双向带头循环链表简图 二、双向带头循环链表的增删查改图解及代码实现 1.双向带头循环链表的头插 2.双向带头循环链表的尾插 3.双向带头循环链表的头删 4.双向带头循环链表的尾删…...
Opencv4基于C++基础入门笔记:OpenCV环境配置搭建
文章目录: 一:软件安装 二:配置环境(配置完之后重启一下软件) 1.配置电脑系统环境变量 vs2012及其以下 vs2014及其以上 2.配置VS软件环境变量 vs2012及其以下 vs2014及其以上 三:测试 vs2012及其…...
JS基础之实现map方法
提示:内容虽少,但是里面也有好几个知识点。 step 1:实现函数 function mapTmp (fn){if(!Array.isArray(this) || !this?.length) return [];const arr [];this.forEach((item, index) > {const newItem fn(item, index, this);arr.pu…...
FPGA应用学习笔记-----复位电路(二)和小结
不可复位触发器若和可复位触发器混合写的话,不可复位触发器是由可复位触发器馈电的。 不应该出现的复位,因为延时导致了冒险,异步复位存在静态冒险 附加素隐含项,利用数电方法,消除静态冒险 这样多时钟区域还是算异步的…...
信捷 XD PLC 16位整数转换为双精度浮点数
完成16位整数转换为双精度浮点数,信捷XD PLC需要两个指令,逐步转换,一个指令搞不定。 具体的: 第1步:int16->int32 第2步:int32->Double 例子,比如说将D0转换成浮点数放到D100~D103...
(二)结构型模式:1、适配器模式(Adapter Pattern)(C++实现示例)
目录 1、适配器模式(Adapter Pattern)含义 2、适配器模式应用场景 3、适配器模式的UML图学习 4、C实现适配器模式的示例 1、适配器模式(Adapter Pattern)含义 将一个接口转换为客户端所期待的接口,从而使两个接口…...
【编程二三事】ES究竟是个啥?
在最近的项目中,总是或多或少接触到了搜索的能力。而在这些项目之中,或多或少都离不开一个中间件 - ElasticSearch。 今天忙里偷闲,就来好好了解下这个中间件是用来干什么的。 ES是什么? ES全称ElasticSearch,是个基于Lucen…...
爬虫逆向实战(三)--天某云登录
一、数据接口分析 主页地址:天某云 1、抓包 通过抓包可以发现登录接口是account/login 2、判断是否有加密参数 请求参数是否加密? 通过“载荷”模块可以发现password、comParam_signature、comParam_seqCode是加密的 请求头是否加密? 无…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
