双指针算法精解:两数之和与三数之和优化实践

王端端

1. 算法精讲:两数之和与三数之和的核心思路与优化

作为一名正在刷题找工作的程序员,我深知算法题中两数之和和三数之和这类经典问题的重要性。这两道题不仅是面试高频考点,更是理解双指针算法的最佳入门案例。本文将结合灵茶山艾府老师的讲解,深入剖析这两道题的解题思路、代码实现和优化技巧。

1.1 两数之和问题解析

1.1.1 问题描述与关键信息

LeetCode 167题"两数之和 II - 输入有序数组"要求我们在一个已排序的数组中找到两个数,使它们的和等于特定目标值。题目给出了三个关键信息:

  1. 数组是非递减顺序排列的(已排序)
  2. 只对应唯一的答案
  3. 需要返回的是数组位置(下标值+1)

这些信息直接决定了我们的解题策略。由于数组已排序,我们可以利用这一特性避免暴力解法,采用更高效的双指针方法。

1.1.2 相向双指针算法详解

相向双指针算法的核心思想是:一个指针从数组起始位置开始(左指针),另一个指针从数组末尾开始(右指针),通过比较两数之和与目标值的关系,逐步向中间移动指针。

具体步骤:

  1. 初始化左指针left=0,右指针right=len(numbers)-1
  2. 计算当前两数之和s = numbers[left] + numbers[right]
  3. 比较s与target:
    • 如果s == target:找到答案,返回指针位置
    • 如果s > target:需要减小和值,故右指针左移(right--)
    • 如果s < target:需要增大和值,故左指针右移(left++)

这个算法的时间复杂度是O(n),空间复杂度是O(1),是最优解法。

注意:由于数组已排序,当s>target时只能移动右指针,s<target时只能移动左指针。这个特性是算法正确性的关键保证。

1.1.3 代码实现与细节分析

python复制class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers) - 1
        while left < right:
            s = numbers[left] + numbers[right]
            if s == target:
                return [left + 1, right + 1]  # 题目要求返回的是位置(下标+1)
            if s > target:
                right -= 1  # 和太大,右指针左移
            else:
                left += 1   # 和太小,左指针右移

代码细节说明:

  1. 循环条件是left < right,确保两个指针不会交叉
  2. 每次循环都重新计算s值,确保使用最新的指针位置
  3. 找到答案后立即返回,因为题目保证有唯一解
  4. 返回的是left+1和right+1,符合题目要求的位置而非下标

2. 三数之和问题深度解析

2.1 问题转化与基本思路

LeetCode 15题"三数之和"要求找出所有不重复的三元组,使得三个数之和为0。与两数之和相比,这道题有三个主要区别:

  1. 数组未排序(需要先排序)
  2. 可能有多个解
  3. 解不能重复

解决思路是将三数之和转化为两数之和问题:

  1. 先对数组排序(O(nlogn)时间)
  2. 固定第一个数nums[i],然后在i+1到末尾的区间内寻找两数之和等于-nums[i]的数对
  3. 使用与两数之和类似的相向双指针方法

2.2 基本解法实现

python复制class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        ans = []
        n = len(nums)
        
        for i in range(n-2):  # 留两个位置给j和k
            x = nums[i]
            # 跳过重复的i值
            if i > 0 and x == nums[i-1]:
                continue
                
            j, k = i+1, n-1
            while j < k:
                s = x + nums[j] + nums[k]
                if s > 0:
                    k -= 1
                elif s < 0:
                    j += 1
                else:
                    ans.append([x, nums[j], nums[k]])
                    j += 1
                    # 跳过重复的j值
                    while j < k and nums[j] == nums[j-1]:
                        j += 1
                    k -= 1
                    # 跳过重复的k值
                    while k > j and nums[k] == nums[k+1]:
                        k -= 1
        return ans

2.3 关键优化技巧

在基本解法基础上,我们可以添加两个重要的提前终止条件,显著提升算法效率:

  1. 最小和检查:如果当前固定的数nums[i]加上后面两个最小的数(nums[i+1]+nums[i+2])已经大于0,那么后面的组合只会更大,可以直接break整个循环。

  2. 最大和检查:如果当前固定的数nums[i]加上最后两个最大的数(nums[-2]+nums[-1])仍然小于0,说明这个i值太小,应该continue到下一个i值。

优化后的代码:

python复制class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        ans = []
        n = len(nums)
        
        for i in range(n-2):
            x = nums[i]
            if i > 0 and x == nums[i-1]:
                continue
                
            # 优化1:最小和检查
            if x + nums[i+1] + nums[i+2] > 0:
                break
                
            # 优化2:最大和检查
            if x + nums[-2] + nums[-1] < 0:
                continue
                
            j, k = i+1, n-1
            while j < k:
                s = x + nums[j] + nums[k]
                if s > 0:
                    k -= 1
                elif s < 0:
                    j += 1
                else:
                    ans.append([x, nums[j], nums[k]])
                    j += 1
                    while j < k and nums[j] == nums[j-1]:
                        j += 1
                    k -= 1
                    while k > j and nums[k] == nums[k+1]:
                        k -= 1
        return ans

3. 算法复杂度分析与对比

3.1 两数之和复杂度分析

  • 时间复杂度:O(n)

    • 最坏情况下需要遍历整个数组一次
    • 每次循环移动一个指针,总共最多移动n-1次
  • 空间复杂度:O(1)

    • 只使用了固定数量的额外空间(两个指针)

3.2 三数之和复杂度分析

  • 时间复杂度:O(n^2)

    • 排序:O(nlogn)
    • 外层循环:O(n)
    • 内层双指针:平均O(n)
    • 总体:O(nlogn) + O(n)*O(n) = O(n^2)
  • 空间复杂度:取决于排序实现

    • Python的sort()使用Timsort算法,空间复杂度O(n)
    • 如果不考虑排序的空间,其他部分使用O(1)空间

3.3 优化前后的性能对比

优化点对性能的影响:

  1. 最小和检查:可以在早期发现无解情况,直接终止循环
  2. 最大和检查:可以跳过明显无解的情况,减少不必要的内层循环

在实际测试中,优化后的解法可以比基本解法快2-3倍,特别是在有大量数据无法满足条件的情况下。

4. 常见问题与调试技巧

4.1 边界条件处理

  1. 空数组或长度不足

    • 两数之和:题目保证有解,但实际代码中应处理len(numbers)<2的情况
    • 三数之和:当n<3时直接返回空列表
  2. 重复元素处理

    • 两数之和:题目保证唯一解,无需处理
    • 三数之和:必须跳过重复的i、j、k值,这是避免重复解的关键

4.2 调试技巧

  1. 打印中间结果

    • 在两数之和中,可以打印每次循环的left、right和s值
    • 在三数之和中,可以打印每次外层循环的i值,以及内层循环的j、k移动情况
  2. 小规模测试用例

    • 两数之和:[2,7,11,15], target=9
    • 三数之和:[-1,0,1,2,-1,-4]
  3. 极端情况测试

    • 全零数组
    • 所有元素相同
    • 无解的情况

4.3 常见错误

  1. 指针移动错误

    • 在两数之和中,当s>target时错误地移动左指针
    • 解决方法:牢记"和太大移动右指针,和太小移动左指针"
  2. 重复解处理不当

    • 在三数之和中,忘记跳过重复的nums[i]、nums[j]、nums[k]
    • 解决方法:在找到解后,继续移动j和k直到遇到不同的值
  3. 下标越界

    • 在三数之和中,i的范围应该是range(n-2)
    • 解决方法:仔细检查循环边界条件

5. 算法扩展与应用

5.1 四数之和问题

同样的思路可以扩展到四数之和问题:

  1. 先排序数组
  2. 固定前两个数
  3. 在后半部分使用双指针寻找剩下的两个数
  4. 注意去重和提前终止条件

5.2 最接近的三数之和

LeetCode 16题"最接近的三数之和"可以使用类似方法:

  1. 排序数组
  2. 固定一个数后使用双指针
  3. 记录最接近目标的和值
  4. 根据当前和与目标的关系移动指针

5.3 实际应用场景

这类算法在实际中有广泛应用:

  1. 统计分析和数据挖掘中的组合查找
  2. 金融领域中的投资组合优化
  3. 计算机图形学中的碰撞检测
  4. 生物信息学中的序列比对

6. 个人刷题心得

在刷这两道题的过程中,我总结了以下几点经验:

  1. 排序是双指针算法的前提:大多数情况下,先排序能让问题变得更简单,虽然排序需要O(nlogn)时间,但往往能换来更高效的解法。

  2. 指针移动要有明确逻辑:每次移动指针都必须有充分的理由,不能随意移动。在两数之和中,移动指针的依据是和与目标值的关系。

  3. 去重是容易被忽略的细节:特别是在三数之和这类问题中,去重逻辑需要仔细处理,否则会得到大量重复解。

  4. 提前终止能显著提升性能:像三数之和中的最小和与最大和检查,虽然看起来是小的优化,但在实际运行中能大幅减少不必要的计算。

  5. 从简单问题入手:理解了两数之和的双指针解法后,三数之和就变成了在两数之和基础上增加一个外层循环,这种循序渐进的学习方式很有效。

最后,建议在理解这些解法后,尝试自己从头实现几次,并在LeetCode上提交测试。只有通过实际编码和调试,才能真正掌握这些算法的精髓。

内容推荐

Yakit热加载的beforeRequest实战:如何像中间人一样实时篡改请求与响应?
本文深入解析Yakit热加载技术中的`beforeRequest`与`afterRequest`功能,展示如何通过Yaklang代码实时篡改HTTP/S请求与响应。从动态修改请求头、参数到响应驱动的自动化测试,详细介绍了渗透测试中的高级应用场景,帮助安全研究人员实现更精细的流量控制与漏洞挖掘。
Silvaco仿真结果怎么看?一文读懂NMOS的Id-Vds曲线和阈值电压提取
本文深入解析Silvaco仿真结果,重点讲解NMOS器件的Id-Vds曲线分析和阈值电压提取技术。通过详细的方法对比和实战案例,帮助半导体工程师掌握参数提取的核心技能,包括恒定电流法、最大跨导法和二次导数法等,提升工艺模拟和器件分析的效率。
美股量化分析:OHLCV数据集应用与策略开发实战
OHLCV(开盘价-最高价-最低价-收盘价-成交量)是金融数据分析的核心数据结构,通过记录资产的价格波动和交易活跃度,为量化策略提供基础数据支持。其原理在于捕捉市场供需关系的动态变化,其中成交量验证价格变动的市场参与度,形成完整的市场行为画像。在量化金融领域,OHLCV数据可用于构建技术指标(如MACD、RSI)、统计套利策略以及机器学习特征工程。以美股历史交易数据为例,包含6192只股票5年周期的780万条记录,能够支持完整的策略回测周期,覆盖牛熊市场转换。该数据集特别适用于配对交易、波动率分析和市场状态建模等应用场景,是量化研究员和算法交易开发者的基础工具。
用Python手搓LDPC译码器:从比特翻转(Bit-Flipping)到和积算法(Sum-Product)的保姆级实现
本文详细介绍了如何使用Python从零实现LDPC译码器,涵盖比特翻转算法和和积算法的保姆级教程。通过NumPy高效处理校验矩阵,展示迭代过程中的消息传递实现,并量化评估不同算法的误码率差异,适合通信系统开发者和算法工程师学习实践。
数据同步工具选型指南:从SeaTunnel、DataX到Flink CDC的实战场景剖析
本文深入剖析了数据同步工具的选型策略,对比了SeaTunnel、DataX和Flink CDC在不同场景下的优劣势。从离线同步到实时处理,详细解析了各工具的核心特性、性能优化技巧及实战配置示例,帮助开发者根据数据规模、实时性要求和技术栈选择最佳解决方案。
从GEO数据到差异基因:一个炎症性肠病(UC)数据集的完整分析复盘与避坑指南
本文详细解析了从GEO数据库获取炎症性肠病(UC)数据集到差异基因分析的全流程,重点介绍了GSE87466数据集的处理方法。通过比较limma与Wilcoxon差异分析策略,探讨logFC计算原理,并提供数据质量控制、结果验证及常见陷阱规避的实用技巧,帮助研究者获得可靠的差异基因列表。
Windows 98系统镜像全版本深度解析与获取指南(特性、差异、兼容性与怀旧应用)
本文深度解析Windows 98系统镜像各版本特性与差异,包括标准版、SE版和企业版,提供详细的获取与验证指南。特别关注Win98SE的改进与兼容性,分享现代硬件上的安装技巧和怀旧应用环境搭建方案,帮助技术爱好者和怀旧玩家完美重现经典系统体验。
Contact-GraspNet: 从4-DoF接触点出发,高效生成杂乱场景的6-DoF抓取
Contact-GraspNet通过创新的4-DoF接触点检测方法,高效生成杂乱场景的6-DoF抓取姿态,成功率超过90%。该系统将复杂抓取问题简化为接触点检测与姿态补全两阶段,大幅提升训练效率和实时性能,适用于仓储、家庭服务等多样化场景。
指令演化实战:用Evol-Instruct策略构建高质量大模型训练数据
本文深入解析Evol-Instruct策略如何通过深度演化和广度演化构建高质量大模型训练数据,提升LLM在垂直领域的表现。从种子数据采集到数据清洗,再到模型微调的关键配方,详细介绍了实战技巧和最佳实践,帮助开发者高效生成专业、多样的指令集。
Linux系统核心操作与性能优化实战指南
Linux操作系统作为现代服务器和云计算基础设施的核心,其性能优化和系统管理是运维工程师的必备技能。从文件系统到进程管理,再到网络配置和存储优化,Linux提供了丰富的工具和参数来调整系统行为。通过理解ext4文件系统的inode分配策略、进程管理的深层机制,以及多队列网卡的中断绑定技术,可以显著提升系统性能。在实际应用中,合理配置LVM快照、选择适当的磁盘调度算法,以及调整内核参数如TCP连接跟踪表和内存管理策略,都是优化系统性能的关键步骤。本文基于2000+服务器运维经验,分享了包括SSD优化、僵尸进程诊断、防火墙策略精调等实战技巧,帮助开发者深入掌握Linux系统调优。
Python实现石头剪刀布游戏:从基础到进阶优化
条件判断和循环控制是编程基础中的核心概念,广泛应用于游戏逻辑实现。通过取模运算可以高效处理环形克制关系,这种算法思想在状态机等场景也有重要应用。随机数生成技术保证了游戏的公平性,而输入验证则体现了健壮性编程的基本原则。以经典的石头剪刀布游戏为例,开发者可以实践数据建模、算法设计和异常处理等关键技能。项目中涉及的策略模式和历史记录功能,展示了如何将简单游戏扩展为可维护的工程化项目,这些经验同样适用于电商促销规则或智能对话系统等实际业务场景。
动态可搜索加密技术:盲存储实现隐私保护检索
可搜索加密技术是云计算安全领域的关键突破,它允许用户在加密数据上执行搜索操作而不泄露隐私信息。其核心原理结合了密码学伪随机函数(如HMAC-SHA256)和分组加密算法(如AES-CTR),通过生成不可逆的关键词指纹实现安全索引。这种技术在工程实践中展现出显著价值,既能满足GDPR等合规要求,又可应用于医疗数据共享、企业文档管理等场景。以盲存储(Blind Storage)为代表的动态方案进一步突破了传统限制,支持增删改查全操作的同时,保证毫秒级的搜索响应速度。实测表明,该方案在百万级文档规模下仍能保持稳定性能,为金融、物联网等行业提供了理想的隐私保护解决方案。
Vite + Vue3 项目实战:深度集成 Monaco Editor 打造高性能在线 IDE
本文详细介绍了如何在Vite + Vue3项目中深度集成Monaco Editor,打造高性能在线IDE。通过实战案例,展示了从基础环境搭建到高级功能实现的完整流程,包括自定义语言支持、主题配置和智能提示等关键功能,帮助开发者快速构建专业级代码编辑环境。
PyTorch detach():从原理到实战,解锁计算图控制的进阶技巧
本文深入解析PyTorch中detach()函数的原理与实战应用,帮助开发者掌握计算图控制的进阶技巧。通过GAN训练、强化学习等场景案例,详细讲解如何正确使用detach()优化模型性能,避免常见陷阱,提升训练效率。适合中高级PyTorch开发者学习梯度传播控制的最佳实践。
CCC数字钥匙实战解析:NFC低功耗检测(LPCD)技术原理与NCF3321芯片应用
本文深入解析了CCC数字钥匙中NFC低功耗检测(LPCD)技术的原理与NCF3321芯片的应用。LPCD技术通过周期性发送射频脉冲实现高效检测,功耗降低90%,响应时间小于100ms。NCF3321芯片集成LPCD和uLPCD双模检测引擎,适用于不同场景需求,为汽车数字钥匙提供智能解决方案。
手把手带你搞定OpenMVS在Windows下的编译部署(VS2022+Vcpkg+CUDA)
本文详细介绍了在Windows系统下使用VS2022和Vcpkg编译部署OpenMVS的完整流程,包括环境配置、依赖库安装、源码获取与CMake配置等关键步骤。通过实战经验分享,帮助开发者高效完成OpenMVS的编译部署,特别针对VS2022与CUDA的兼容性问题提供了解决方案。
数理统计核心考点解析:从理论推导到实际应用
数理统计作为数据分析的基础学科,其核心在于通过概率模型描述随机现象,并基于样本数据做出科学推断。统计推断主要包括参数估计和假设检验两大方法,其中极大似然估计(MLE)通过最大化似然函数寻找最优参数,而贝叶斯统计则引入先验分布实现概率更新。在实际工程中,回归分析用于建立变量间定量关系,模型诊断技术可识别异方差等常见问题。以武汉大学研究生试题为例,考题设计特别强调统计计算实现能力,涉及Weibull分布参数估计、假设检验功效分析等典型场景,反映出高等教育对'理论+实践'复合能力的要求。掌握这些核心方法,不仅能应对学术考核,更能为机器学习、生物统计等领域的实际问题提供解决方案。
LED平板灯技术解析与选型指南
LED照明技术作为现代节能照明的核心解决方案,其核心原理是通过半导体发光实现高效光电转换。在工程实践中,光效、均匀度和眩光控制构成评价LED平板灯品质的三大关键技术指标。其中光效提升依赖芯片效率与光学系统优化,而微棱晶扩散板等创新设计可将光线均匀度提升至0.9以上。这些技术进步使得LED平板灯在办公、教育、医疗等场景中展现出显著优势,特别是结合智能控制系统后,可实现视觉舒适度与节能效果的完美平衡。当前行业正朝着150流明/瓦的光效目标突破,量子点技术和自适应光学系统将成为下一代产品的关键技术方向。
从PyTorch到MATLAB:YOLOv5 ONNX模型迁移部署的避坑指南与实战
本文详细介绍了将YOLOv5模型从PyTorch迁移到MATLAB的完整流程,重点解析了ONNX格式转换、MATLAB环境配置、数据预处理与后处理的精确对齐等关键技术环节。通过实战案例和避坑指南,帮助开发者高效实现目标检测模型的跨平台部署,提升工业环境中的模型应用效率。
新能源电网中Q(V)-控制策略的Matlab实现与稳定性分析
在新能源高渗透率的现代电网中,电力电子变流器的广泛应用带来了电压稳定性新挑战。Q(V)-控制作为一种先进的无功-电压下垂控制策略,通过分段函数实现动态无功调节,能有效抑制多逆变器间的无功环流问题。该技术采用硬件在环与数字仿真结合的方式,在Matlab/Simulink中搭建IEEE 33节点测试系统,并引入动态阻抗扫描法分析系统阻尼特性。工程实践中,通过差异化斜率设计和三重滤波方案解决了参数整定和测量噪声等关键问题,为新能源电站并网提供了可靠解决方案。
已经到底了哦
精选内容
热门内容
最新内容
马年春节金曲制作技术与文化创新解析
音乐制作中的文化元素融合是现代创作的重要方向,特别是在节日主题作品中。通过数字音频技术与传统民乐的结合,可以实现既有文化底蕴又符合现代审美的音乐作品。在技术层面,采样处理、和声编排和音效设计是关键,如使用滤波处理马蹄声、五声音阶变形创作旋律等。这类制作方法不仅能提升作品质量,还能增强文化传播效果。春节歌曲作为特定场景的音乐产品,需要平衡商业性、艺术性和文化性,马年金曲榜项目正是这种平衡的典范。该案例展示了如何通过侧链压缩模拟节奏、民乐现代化改编等技术手段,实现传统生肖文化的创新表达,为音乐制作人提供了节日音乐创作的实用方法论。
Blender曲线进阶:从Logo设计到动画路径的实战指南
本文详细介绍了Blender曲线工具的高级应用,从Logo设计到动画路径规划的实战技巧。通过贝塞尔曲线和NURBS曲线的核心操作,结合Logo设计案例和相机路径动画,帮助用户掌握Blender曲线建模与特效应用,提升3D创作效率。
别再死记硬背了!用这10个KVM高频面试题+实战命令,搞定运维面试
本文深入解析KVM虚拟化技术的10大高频面试题及实战命令,帮助运维工程师高效准备技术面试。内容涵盖KVM核心架构、存储镜像管理、网络配置优化及高级排错技巧,特别强调常用命令的实际应用场景,助你展现专业实力。
从美术原理到GIS实操:手把手教你用ArcMap图层叠加与透明度,调出专业地形图
本文详细解析如何利用ArcMap的图层叠加与透明度调节技术,结合DEM数据和山体阴影工具,制作专业地形图。通过美术原理与GIS技术的融合,提升地形图的视觉表现力与科学性,涵盖从基础操作到高级渲染技巧的全流程指导,助力地质勘探、城市规划等领域的专业制图需求。
解锁6.6kW OBC高功率密度:基于GaN的驱动、热管理与谐振拓扑实战解析
本文深入解析了基于氮化镓(GaN)技术的6.6kW车载充电器(OBC)设计,重点探讨了GaN在高频开关、驱动设计、热管理和CLLLC谐振拓扑中的应用优势。通过实战案例和详细的技术参数,展示了如何实现高功率密度和高效能的热管理方案,为电动车充电模块的设计提供了宝贵经验。
从MTTF、MTBF到MTTR:构建系统可靠性的黄金三角
本文深入解析MTTF、MTBF和MTTR三大关键指标,揭示它们如何共同构建系统可靠性的黄金三角。通过实际案例分享,详细阐述如何提升MTTF(平均失效前时间)、优化MTBF(平均故障间隔时间)以及缩短MTTR(平均修复时间),帮助架构师和运维团队实现系统可靠性的动态平衡与持续改进。
《小狗钱钱》财富密码:从零到一构建你的个人财务操作系统
本文解析《小狗钱钱》中的财富密码,教你从零构建个人财务操作系统。从梦想目标设定、开源债务处理到资源分配和投资增值,系统化指导实现财务自由。重点介绍养鹅账户、梦想储蓄等实用方法,帮助读者建立可持续的财富增长机制。
别急着删.condarc!Conda报错‘Retrieving notices failed’的三种修复思路与原理详解
本文详细解析了Conda报错‘Retrieving notices failed’的三种修复思路与原理,帮助开发者理解Conda notices机制。从网络层问题排查、配置层深度解析到运行时层高级技巧,提供系统性解决方案,避免简单删除.condarc文件的粗暴做法。
DeblurGANv2复现实战:从环境配置到效果评估
本文详细介绍了DeblurGANv2的复现过程,从环境配置到效果评估的全流程实战指南。重点讲解了PyTorch环境搭建、GOPRO数据集处理、模型训练参数调优以及PSNR/SSIM定量评估方法,帮助开发者高效实现图像去模糊任务。
高速公路强声定向广播系统技术与应用解析
定向声学技术通过参量阵扬声器产生高度定向的声波,解决了传统广播系统噪音污染和语音清晰度问题。其核心技术在于利用超声波自解调效应,实现±15°内的精准声束控制,声压级可达110dB@1m。在智能交通领域,该技术显著提升了高速公路预警效率,特别适用于团雾预警和事故处置等场景。系统采用分层架构设计,包含中心控制、网络传输和现场设备层,通过光纤环网确保传输可靠性。实际应用数据显示,该系统能将事故预警响应时间缩短至8秒,二次事故发生率下降62%,同时实现零噪音投诉。