编程题-最接近的三数之和
题目:
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。

解法一(排序+双指针):
题目要求找到与目标值 target 最接近的三元组,这里的「最接近」即为差值的绝对值最小。我们可以考虑直接使用三重循环枚举三元组,找出与目标值最接近的作为答案,时间复杂度为 O(N^3)。然而本题的 N 最大为 1000,会超出时间限制。
我们首先枚举第一个元素 a1,对于剩下的两个元素 a2和 a3,希望它们的和最接近target−a1。对于 a2 和 a3,如果它们在原数组中枚举的范围(既包括下标的范围,也包括元素值的范围)没有任何规律可言,那么我们还是只能使用两重循环来枚举所有的可能情况。因此,我们可以考虑对整个数组进行升序排序,这样一来
-
假设数组的长度为 n,我们先枚举 a1,它在数组中的位置为 i;
-
为了防止重复枚举,我们在位置 [i+1,n) 的范围内枚举 a2 和 a3。
当我们知道了a2和a3可以枚举的下标范围,并且知道这一范围对应的数组元素是有序(升序)的,那么我们是否可以对枚举的过程进行优化呢?
借助双指针对枚举的过程进行优化,我们用a2和a3作为双指针,初始时,a2指向位置i+1,即左边界,a3指向位置n-1,即右边界。在每一步枚举过程中,我们采用a1+a2+a3来更新答案,并且
-
如果 a1+a2+a3≥target,那么就将 a3 向左移动一个位置;
-
如果 a1+a2+a3<target,那么就将 a2 向右移动一个位置。
这是为什么呢,我们对 a1+a2+a3≥target的情况进行详细的分析:
如果a1+a2+a3≥target,并且我们知道a2和a3这个范围是按照升序排列的,那么如果a3不变而移动a2向右,那么a1+a2+a3的值就会不断地增加,显然就不会成为最接近target的值了。因此,我们可以知道在固定a3的情况下,此时的a2就可以得到一个最接近target的值了,那么我们以后就不用再考虑a3了,就可以将a3向左移动一个位置。
同样地,a1+a2+a3<target 时,如果a2不变而a3向左移动,那么a1+a2+a3的值就会不断地减小,显然就不会成为最接近target的值了。因此,我们可以知道固定了a2的情况下,此时的a3就可以得到一个最接近target的值,那么我们以后就不用再考虑a2了,就可以将a2向右移动一个位置。
实际上,a2和a3就表示我们当前选择的数的范围,而每一次枚举的过程中,我们尝试边界上的两个元素,根据它们与target的值的关系,选择【抛弃】左边界的元素还是右边界的元素,从而减少了枚举的范围。这种思路与【盛最多水的容器】中的双指针解法也是类似的。当我们枚举,a1,a2,a3 中任意元素并移动指针时,可以直接将其移动到下一个与这次枚举得到的不相同的元素,减少枚举的次数,如下代码为:
class Solution {
public:int threeSumClosest(vector<int>& nums, int target) {sort(nums.begin(), nums.end());int n = nums.size();int best = 1e7;// 根据差值的绝对值来更新答案auto update = [&](int cur) {if (abs(cur - target) < abs(best - target)) {best = cur;}};// 枚举 afor (int i = 0; i < n; ++i) {// 保证和上一次枚举的元素不相等if (i > 0 && nums[i] == nums[i - 1]) {continue;}// 使用双指针枚举 b 和 cint j = i + 1, k = n - 1;while (j < k) {int sum = nums[i] + nums[j] + nums[k];// 如果和为 target 直接返回答案if (sum == target) {return target;}update(sum);if (sum > target) {// 如果和大于 target,移动 c 对应的指针int k0 = k - 1;// 移动到下一个不相等的元素while (j < k0 && nums[k0] == nums[k]) {--k0;}k = k0;} else {// 如果和小于 target,移动 b 对应的指针int j0 = j + 1;// 移动到下一个不相等的元素while (j0 < k && nums[j0] == nums[j]) {++j0;}j = j0;}}}return best;}
};
时间复杂度:O(N2),其中 N 是数组 nums 的长度。我们首先需要 O(NlogN) 的时间对数组进行排序,随后在枚举的过程中,使用一重循环 O(N) 枚举 a,双指针 O(N) 枚举 b 和 c,故一共是 O(N2)。
空间复杂度:O(logN)。排序需要使用 O(logN) 的空间。然而我们修改了输入的数组 nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,此时空间复杂度为 O(N)。
下面代码是笔者在编程时所写的,虽然时间复杂度没有超限,但是相比上面代码在时间复杂度上面仍然是消耗时间比较大的,但是空间复杂度上面比上面代码占用消耗是较小的。其中第二层循环中思路也是:如果和小于target,则移动a2向右移动,进入下一次循环;如果和大于target,则移动a3向左移动,执行while循环,实现原理通过增加条件判断语句使得双指针(左边界指针、右边界指针)两个指针朝着相遇的方向进行移动(减少时间复杂度,防止重复遍历),但是阅读理解代码起来较为复杂,同样是作为正确的解决思路与上面方法进行对比,如下为笔者代码:
class Solution {
public:int threeSumClosest(vector<int>& nums, int target) {//定义输出结果result值int min_value = 1000000, length=nums.size();int result=0;//将nums数组由小到大重新进行排序sort(nums.begin(), nums.end());//循环遍历查找满足条件要求的结果for(int a1=0; a1<length-2; a1++){if(a1>1 && nums[a1]==nums[a1-1]){continue;}for(int a2=a1+1; a2<length-1;a2++){if(a2>a1+1 && nums[a2]==nums[a2-1]){continue;}int a3 = length-1;//如果和小于target,则移动a2向右移动(进入下一层循环)if(nums[a1]+nums[a2]+nums[a3]<target){result=min_value>abs(nums[a1]+nums[a2]+nums[a3]-target)?(nums[a1]+nums[a2]+nums[a3]):result;min_value = min(abs(nums[a1]+nums[a2]+nums[a3]-target), min_value);continue;}while(a2<a3){if(nums[a1]+nums[a2]+nums[a3]<target){result=min_value>abs(nums[a1]+nums[a2]+nums[a3]-target)?(nums[a1]+nums[a2]+nums[a3]):result;min_value = min(abs(nums[a1]+nums[a2]+nums[a3]-target), min_value);result=min_value>abs(nums[a1]+nums[a2]+nums[a3+1]-target)?(nums[a1]+nums[a2]+nums[a3+1]):result;min_value = min(abs(nums[a1]+nums[a2]+nums[a3+1]-target), min_value);break;}//如果和大于target,则移动a3向左移动(执行while循环)else{result=min_value>abs(nums[a1]+nums[a2]+nums[a3]-target)?(nums[a1]+nums[a2]+nums[a3]):result;min_value = min(abs(nums[a1]+nums[a2]+nums[a3]-target), min_value);a3--;}}}}return result;}
};
笔者小记:
1、借助双指针对枚举的过程进行优化,降低多重循环导致的时间复杂度。对于本题,时间复杂度可由O(N^3)降低至O(N^2)。
相关文章:
编程题-最接近的三数之和
题目: 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 解法一(排序双指针): 题目要求找…...
索引的底层数据结构、B+树的结构、为什么InnoDB使用B+树而不是B树呢
索引的底层数据结构 MySQL中常用的是Hash索引和B树索引 Hash索引:基于哈希表实现的,查找速度非常快,但是由于哈希表的特性,不支持范围查找和排序,在MySQL中支持的哈希索引是自适应的,不能手动创建 B树的…...
【工欲善其事】利用 DeepSeek 实现复杂 Git 操作:从原项目剥离出子版本树并同步到新的代码库中
文章目录 利用 DeepSeek 实现复杂 Git 操作1 背景介绍2 需求描述3 思路分析4 实现过程4.1 第一次需求确认4.2 第二次需求确认4.3 第三次需求确认4.4 V3 模型:中间结果的处理4.5 方案验证,首战告捷 5 总结复盘 利用 DeepSeek 实现复杂 Git 操作 1 背景介绍…...
网络编程套接字(中)
文章目录 🍏简单的TCP网络程序服务端创建套接字服务端绑定服务端监听服务端获取连接服务端处理请求客户端创建套接字客户端连接服务器客户端发起请求服务器测试单执行流服务器的弊端 🍐多进程版的TCP网络程序捕捉SIGCHLD信号让孙子进程提供服务 …...
前端学习-事件委托(三十)
目录 前言 课前思考 for循环注册事件 语法 事件委托 1.事件委托的好处是什么? 2.事件委托是委托给了谁,父元素还是子元素 3.如何找到真正触发的元素 示例代码 总结 前言 才子佳人,自是白衣卿相 课前思考 1.如果同时给多个元素注册事件&…...
线程池以及在QT中的接口使用
文章目录 前言线程池架构组成**一、任务队列(Task Queue)****二、工作线程组(Worker Threads)****三、管理者线程(Manager Thread)** 系统协作流程图解 一、QRunnable二、QThreadPool三、线程池的应用场景W…...
c语言操作符(详细讲解)
目录 前言 一、算术操作符 一元操作符: 二元操作符: 二、赋值操作符 代码例子: 三、比较操作符 相等与不相等比较操作符: 大于和小于比较操作符: 大于等于和小于等于比较操作符: 四、逻辑操作符 逻辑与&…...
【自然语言处理(NLP)】深度学习架构:Transformer 原理及代码实现
文章目录 介绍Transformer核心组件架构图编码器(Encoder)解码器(Decoder) 优点应用代码实现导包基于位置的前馈网络残差连接后进行层规范化编码器 Block编码器解码器 Block解码器训练预测 个人主页:道友老李 欢迎加入社…...
JavaScript 入门教程
JavaScript 入门教程 JavaScript 入门教程引言学习 JavaScript 的好处常见的 JavaScript 框架和库 安装开发环境下载并安装 Node.js 和 npm安装常用开发工具(如 VS Code)配置本地开发环境 基础语法入门数据类型变量与常量运算符算术运算符比较运算符 条件…...
浅析CDN安全策略防范
CDN(内容分发网络)信息安全策略是保障内容分发网络在提供高效服务的同时,确保数据传输安全、防止恶意攻击和保护用户隐私的重要手段。以下从多个方面详细介绍CDN的信息安全策略: 1. 数据加密 数据加密是CDN信息安全策略的核心之…...
代码随想录刷题day22|(字符串篇)344.反转字符串、541.反转字符串 II
目录 一、题目思路 二、相关题目 三、总结与知识点 3.1 字符数组转换成字符串 一、题目思路 344反转字符串比较容易,双指针即可在空间复杂度为O(1)的基础上解决; 541反转字符串II :其中for循环中 i 每次的取值,不是 i&#…...
python学opencv|读取图像(五十三)原理探索:使用cv.matchTemplate()函数实现最佳图像匹配
【1】引言 前序学习进程中,已经探索了使用cv.matchTemplate()函数实现最佳图像匹配的技巧,并且成功对两个目标进行了匹配。 相关文章链接为:python学opencv|读取图像(五十二)使用cv.matchTemplate()函数实现最佳图像…...
win10部署本地deepseek-r1,chatbox,deepseek联网(谷歌网页插件Page Assist)
win10部署本地deepseek-r1,chatbox,deepseek联网(谷歌网页插件Page Assist) 前言一、本地部署DeepSeek-r1step1 安装ollamastep2 下载deepseek-r1step2.1 找到模型deepseek-r1step2.2 cmd里粘贴 后按回车,进行下载 ste…...
冯·诺依曼体系结构
目录 冯诺依曼体系结构推导 内存提高冯诺依曼体系结构效率的方法 你使用QQ和朋友聊天时,整个数据流是怎么流动的(不考虑网络情况) 与冯诺依曼体系结构相关的一些知识 冯诺依曼体系结构推导 计算机的存在就是为了解决问题,而解…...
本地部署 DeepSeek-R1 模型
文章目录 霸屏的AIDeepSeek是什么?安装DeepSeek安装图形化界面总结 霸屏的AI 最近在刷视频的时候,总是突然突然出现一个名叫 DeepSeek 的玩意,像这样: 这样: 这不经激起我的一顿好奇心,这 DeepSeek 到底是个…...
Mybatis——sql映射文件中的增删查改
映射文件内的增删查改 准备工作 准备一张数据表,用于进行数据库的相关操作。新建maven工程, 导入mysql-connector-java和mybatis依赖。新建一个实体类,类的字段要和数据表的数据对应编写接口编写mybatis主配置文件 public class User {priva…...
【开源免费】基于Vue和SpringBoot的流浪宠物管理系统(附论文)
本文项目编号 T 182 ,文末自助获取源码 \color{red}{T182,文末自助获取源码} T182,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...
nth_element函数——C++快速选择函数
目录 1. 函数原型 2. 功能描述 3. 算法原理 4. 时间复杂度 5. 空间复杂度 6. 使用示例 8. 注意事项 9. 自定义比较函数 11. 总结 nth_element 是 C 标准库中提供的一个算法,位于 <algorithm> 头文件中,用于部分排序序列。它的主要功能是将…...
DNS缓存详解(DNS Cache Detailed Explanation)
DNS缓存详解 清空DNS缓存可以让网页访问更快捷。本文将从什么是DNS缓存、为什么清空DNS缓存、如何清空DNS缓存、清空DNS缓存存在的问题四个方面详细阐述DNS缓存清空的相关知识。 一、什么是DNS缓存 1、DNS缓存的定义: DNS缓存是域名系统服务在遇到DNS查询时自动…...
课设:【ID0022】火车票售票管理系统(前端)
技术栈:Java,JavaSwing,MySQL 数据库表数量:12个 1.功能描述 管理员功能 管理员是系统的高级用户,拥有对整个系统的全面管理权限。管理员的功能模块包括以下六个方面: 对用户管理增删查改 对售票员…...
从《西部世界》到现实:AI智能体如何重塑游戏NPC与虚拟社会?
从《西部世界》到现实:AI智能体如何重塑游戏NPC与虚拟社会? 当《西部世界》中的NPC开始拥有记忆、情感和自主决策能力时,观众惊叹于科幻与现实的边界正在模糊。如今,大型语言模型(LLM)驱动的AI智能体正将这…...
5分钟免费制作专业AI翻唱:AICoverGen完整指南
5分钟免费制作专业AI翻唱:AICoverGen完整指南 【免费下载链接】AICoverGen A WebUI to create song covers with any RVC v2 trained AI voice from YouTube videos or audio files. 项目地址: https://gitcode.com/gh_mirrors/ai/AICoverGen 想让AI帮你翻唱…...
Applite:告别命令行!macOS软件管理的图形化终极解决方案
Applite:告别命令行!macOS软件管理的图形化终极解决方案 【免费下载链接】Applite User-friendly GUI macOS application for Homebrew Casks 项目地址: https://gitcode.com/gh_mirrors/ap/Applite 还在为Homebrew复杂的命令行操作而头疼吗&…...
如何快速掌握openpilot:从零到精通的自动驾驶系统终极指南
如何快速掌握openpilot:从零到精通的自动驾驶系统终极指南 【免费下载链接】openpilot openpilot is an operating system for robotics. Currently, it upgrades the driver assistance system on 300 supported cars. 项目地址: https://gitcode.com/GitHub_Tre…...
ViGEmBus终极指南:Windows游戏手柄模拟驱动的完整解决方案
ViGEmBus终极指南:Windows游戏手柄模拟驱动的完整解决方案 【免费下载链接】ViGEmBus Windows kernel-mode driver emulating well-known USB game controllers. 项目地址: https://gitcode.com/gh_mirrors/vi/ViGEmBus 你是否曾经遇到过这样的情况ÿ…...
Blitz.js全栈开发框架:零API理念与Next.js深度集成实战
1. 项目概述:一个颠覆性的全栈开发框架如果你和我一样,在过去的几年里,一直在React生态圈里打转,从Create React App到Next.js,再到尝试自己搭建一套包含身份验证、数据层、API路由的完整应用,那你一定对那…...
量化交易强化学习环境TradingGym:从Gym接口到实战策略训练
1. 项目概述:一个为量化交易策略量身定制的强化学习训练场如果你正在尝试将强化学习(Reinforcement Learning, RL)应用到股票、期货或加密货币的量化交易中,大概率会遇到一个共同的困境:环境太难搭了。市面上的回测框架…...
Apache Burr:用状态机模式构建Python流式应用
1. 项目概述:一个用于构建流式应用的Python框架最近在折腾一些实时数据处理和模型推理的项目,从简单的日志分析到复杂的在线推荐,总感觉现有的工具链要么太重,要么太散。想要一个既能处理流式数据,又能轻松集成机器学习…...
【STC8H】GPIO模式深度解析:从准双向到推挽,如何精准控制外设
1. STC8H的GPIO模式全景解析 第一次接触STC8H的GPIO配置时,我被那个神秘的PxM0和PxM1寄存器搞得晕头转向。直到有一次调试I2C通讯失败,才发现是开漏模式配置错误。这次教训让我明白,理解GPIO的四种工作模式,就像掌握不同武器的使用…...
AI智能体任务控制中心:构建可管理复杂项目的协作框架
1. 项目概述:为智能体装上“任务控制中心” 最近在折腾AI智能体(Agent)开发的朋友,可能都遇到过这样的场景:你精心设计了一个能联网搜索、处理文档、调用API的智能体,它单次任务的表现堪称完美。但当你试图…...
