【数据结构】双指针算法:理论与实战

news2024/10/27 10:53:13

双指针(Two Pointers)是一种经典的算法思想,广泛应用于数组、链表等数据结构的处理。该方法通过设置两个指针,在某种规则下移动指针来实现高效的计算与查找。这种算法相比传统的嵌套循环能显著优化时间复杂度,通常能够达到 O(N) 的时间复杂度,从而大幅提升效率。

如果你还不了解什么是时间复杂度,可参考以下文章:

  • 【数据结构】时间复杂度和空间复杂度是什么?

双指针算法的核心思想

双指针的基本思路是使用两个指针同时操作,以减少重复计算。两个指针可以根据具体情况分为同向指针和对向指针。

  1. 同向指针:两个指针从一端出发,彼此逐步靠近。例如,用于查找数组中满足条件的连续子数组。
  2. 对向指针:两个指针分别从两端出发,逐步相向移动,适用于查找满足条件的元素组合,如求数组中和为特定值的两个数。

通过双指针的移动,我们可以逐步缩小问题的范围,避免重复计算。它的应用场景广泛,通常用于解决有序数组的查找、链表的判环等问题。

算法步骤

  1. 初始化两个指针:通常从数组的两端出发(对向指针)或从数组的同一端出发(同向指针)。
  2. 根据问题的条件决定指针的移动方向。例如,若寻找数组中和为特定值的两数,若当前和大于目标值则移动右指针,反之则移动左指针。
  3. 在每一步迭代中,检查当前指针位置是否满足目标条件,若满足则记录结果或停止算法。
  4. 重复此过程,直到两个指针相遇或无法满足条件为止。

算法模板

以下是双指针算法的基本模板:

int twoPointerExample(const vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            return {left, right};  // 找到目标
        } else if (sum < target) {
            left++;  // 增大和
        } else {
            right--; // 减小和
        }
    }
    return {};  // 无解
}

代码解析

  1. 初始化左右指针left 初始化为数组起始位置 0right 初始化为数组末尾位置 nums.size() - 1。双指针从两端相向移动,逐步缩小搜索范围。

  2. 循环条件while (left < right) 保证了左指针位于右指针左侧。当左右指针重叠时,说明已遍历所有可能组合。

  3. 计算当前和并判断:在每次循环中,计算当前指针位置的和 sum = nums[left] + nums[right],并判断是否等于目标值 target。若相等则直接返回索引,表示找到了满足条件的解。

  4. 调整指针位置:若 sum < target,表示当前和小于目标值,需要增大和,因此将 left 右移。反之,若 sum > target,则将 right 左移以减小和。通过这种调整,算法在满足条件的前提下高效地缩小搜索范围。

  5. 无解返回:若循环结束未找到解,返回空数组 return {};。这种处理方式可以避免出现异常情况并确保程序的稳定性。

注意事项

  1. 指针范围与数组有序性:该示例依赖于输入数组的有序性,以确保双指针能够正确缩小范围。若数组无序,则无法正确缩小搜索区间。
  2. 边界情况:若数组为空或仅有一个元素,则算法无需运行或会直接返回无解结果。
  3. 时间复杂度:由于每次操作仅需移动 leftright 指针之一,整个过程的时间复杂度为 O(N),相较于暴力求解的 O(N^2) 更加高效。
  4. 空间复杂度:本算法无需额外空间,空间复杂度为 O(1)。

算法实战:盛最多水的容器

给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])。要求找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

输入示例

height = [1,8,6,2,5,4,8,3,7]

输出

49

解释
在这里插入图片描述

在此情况下,最大水容量为 49,选择的两个端点分别在位置 1 和位置 8。

代码实现

class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0, r = height.size() - 1, ans = 0;
        while (l < r) {
            ans = max(ans, (r - l) * min(height[r], height[l]));
            if (height[r] < height[l]) r--;
            else l++;
        }
        return ans; 
    }
};

代码解析

  1. lr 初始化为左右两端的指针。
  2. ans 用于存储最大面积,初始为 0。
  3. while(l < r):当左右指针相遇时停止迭代。
  4. 每次迭代中更新 ans 为当前最大面积。
  5. 比较 height[l]height[r],移动较短的一端的指针,直到 l >= r

复杂度分析

  • 时间复杂度:O(N),因为只需要遍历数组一次。
  • 空间复杂度:O(1),除了输入外不需要额外的空间。

双指针的拓展

除了经典的求和问题,双指针还可以用于许多其他场景:

  • 链表的环检测:快慢指针用于判断链表是否存在环。
  • 分割数组:例如分割奇偶数组,通过同向双指针将奇数与偶数分开。
  • 字符串处理:用于判断回文串、删除字符串中的某些字符等问题。

注意事项

  1. 指针范围控制:使用双指针时需谨慎控制指针范围,以防止越界。
  2. 初始条件:在一些问题中需设定合适的初始条件,如链表判环问题中的初始 slow 和 fast 指针。

总结

双指针算法是一种简洁而高效的算法思路,广泛应用于数组和链表的处理。理解双指针的原理和不同应用场景,可以帮助我们在算法竞赛和日常编程中解决复杂问题。通过合理控制两个指针的移动,可以极大地降低时间复杂度,避免不必要的重复计算。

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

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

相关文章

python读取学术论文PDF文件内容

目录 1、PyPDF22、pdfplumber3、PyMuPDF4、pdfminer总结 1、PyPDF2 PyPDF2 是一个常用的库&#xff0c;可以用来读取、合并、分割和修改PDF文件。读取pdf内容&#xff1a; import PyPDF2# 打开PDF文件 with open(ELLK-Net_An_Efficient_Lightweight_Large_Kernel_Network_for…

ThriveX 现代化博客管理系统

ThriveX 现代化博客管理系统 &#x1f389; &#x1f525; 首先最重要的事情放第一 开源不易&#xff0c;麻烦占用 10 秒钟的时间帮忙点个免费的 Star&#xff0c;再此万分感谢&#xff01; 下面开始进入主题↓↓↓ &#x1f308; 项目介绍&#xff1a; Thrive 是一个简而不…

行为设计模式 -责任链模式- JAVA

责任链设计模式 一 .简介二. 案例2.1 抽象处理者(Handler)角色2.2 具体处理者(ConcreteHandler)角色2.3 测试 三. 结论3.1 优缺点3.2 示例3.3 要点 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神…

xxl-job java.sql.SQLException: interrupt问题排查

近期生产环境固定凌晨报错&#xff0c;提示 ConnectionManager [Thread-23069] getWriteConnection db:***,pattern: error, jdbcUrl: jdbc:mysql://***:3306/***?connectTimeout3000&socketTimeout180000&autoReconnecttrue&zeroDateTimeBehaviorCONVERT_TO_NUL…

面试域——岗位职责以及工作流程

摘要 介绍互联网岗位的职责以及开发流程。在岗位职责方面&#xff0c;详细阐述了产品经理、前端开发工程师、后端开发工程师、测试工程师、运维工程师等的具体工作内容。产品经理负责需求收集、产品规划等&#xff1b;前端专注界面开发与交互&#xff1b;后端涉及系统架构与业…

本地缓存库分析(一):golang-lru

文章目录 本地缓存概览golang-lru标准lrulru的操作PutGet 2q&#xff1a;冷热分离lruPutGet expirable_lru&#xff1a;支持过期时间的lruPutGet过期 总结 本地缓存概览 在业务中&#xff0c;一般会将极高频访问的数据缓存到本地。以减少网络IO的开销&#xff0c;下游服务的压…

【OpenAI】第五节(图像生成)利用 OpenAI 的 DALL·E 实现自动化图像生成:从文本到图像的完整教程

引言 OpenAI 推出的 DALLE 工具因其能够生成令人惊叹的艺术作品而备受瞩目。DALLE 不仅能够生成静态图像&#xff0c;还能根据用户的需求进行风格化处理&#xff0c;创造出独特的艺术作品。通过 OpenAI 的 API&#xff0c;你可以轻松将 DALLE 的强大功能集成到你的 Python 程序…

基于SSM的智慧篮球馆预约系统

前言 近些年&#xff0c;随着中国经济发展&#xff0c;人民的生活质量逐渐提高&#xff0c;对网络的依赖性越来越高&#xff0c;通过网络处理的事务越来越多。随着智慧篮球馆预约的常态化&#xff0c;如果依然采用传统的管理方式&#xff0c;将会为工作人员带来庞大的工作量&a…

Linux中C/C++程序编译过程与动静态链接库概述

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

qt QMainWindow详解

一、概述 QMainWindow继承自QWidget&#xff0c;并提供了一个预定义的布局&#xff0c;将窗口分成了菜单栏、工具栏、状态栏和中央部件区域。这些区域共同构成了一个功能丰富的主窗口&#xff0c;使得应用程序的开发更加简单和高效。 二、QMainWindow的常用组件及功能 菜单栏&…

【Java网络编程】从套接字(Socket)概念到UDP与TCP套接字编程

目录 网络编程 1.socket套接字 2.udp数据报套接字编程 DatagramSocket API DatagramPacket API Java基于UDP实现客户端-服务器代码实例 3.tcp流套接字编程 ServerSocket API Socket API TCP中的长短连接 Java基于TCP客户端-服务器代码实例 网络编程 1.socket套接字 S…

云对象存储进阶

《使用Minio搭建文件服务器》一文对minio作了简单的介绍&#xff0c;本文为进阶学习。 1.对象存储产品介绍 目前市场上流行各种对象存储服务&#xff0c;诸如以下&#xff1a; Amazon S3&#xff1a;亚马逊提供的服务&#xff0c; 是市场上最成熟的产品&#xff0c;拥有最大的…

ATom:2016-2018 年沿飞行轨迹的 CAM-chem/CESM2 模型输出

目录 简介 摘要 代码 引用 网址推荐 知识星球 机器学习 ATom: CAM-chem/CESM2 Model Outputs Along Flight Tracks, 2016-2018 ATom&#xff1a;2016-2018 年沿飞行轨迹的 CAM-chem/CESM2 模型输出 简介 该数据集包含沿 ATom 飞行轨迹的 CAM-chem&#xff08;带化学的…

[ARM-2D 专题]5 MDK编译器一个旧版本-Ofast优化bug的问题及解决办法

最近开始大量基于ARM-2D开发应用项目&#xff0c;为了达到最佳性能&#xff0c;我们使用了编译器的许多特殊技能&#xff0c;其中就包含了-Ofast优化&#xff0c;很不幸&#xff0c;一不小心踩坑了。 案发情况如下&#xff1a; 使用的MDK版本5.36&#xff0c;编译器6.16 优化选…

在GeoTools中的Shapefile属性表读取效率之Shp与Dbf对比

目录 前言 一、POI测试数据简介 1、选用的POI数据 2、关于数据的属性数据 二、属性数据读取的两种方式实现 1、基于DbaseFileReader的读取 2、基于SimpleFeatureSource的读取 三、实际运行对比 1、内存和CPU占用情况 2、运行耗时情况 四、总结 前言 众所周知&#x…

【深度学习中的注意力机制10】11种主流注意力机制112个创新研究paper+代码——交叉注意力(Cross-Attention)

【深度学习中的注意力机制10】11种主流注意力机制112个创新研究paper代码——交叉注意力&#xff08;Cross-Attention&#xff09; 【深度学习中的注意力机制10】11种主流注意力机制112个创新研究paper代码——交叉注意力&#xff08;Cross-Attention&#xff09; 文章目录 【…

‌Spring MVC的主要组件有哪些?

前言 SpringMVC的核心组件包括DispatcherServlet、Controller、HandlerMapping、HandlerAdapter、ViewResolver、ModelAndView等&#xff0c;它们协同工作以支持基于MVC架构的Web应用程序开发。这些组件使得开发人员能够以一种声明式和模块化的方式构建Web应用程序&#xff0c…

小程序开发实战:PDF转换为图片工具开发

目录 一、开发思路 1.1 申请微信小程序 1.2 编写后端接口 1.3 后端接口部署 1.4 微信小程序前端页面开发 1.5 运行效果 1.6 小程序部署上线 今天给大家分享小程序开发系列&#xff0c;PDF转换为图片工具的开发实战&#xff0c;感兴趣的朋友可以一起来学习一下&#xff01…

ECharts饼图-基础南丁格尔玫瑰图,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个饼图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供详…

一、在cubemx下RTC配置调试实例测试

一、rtc的时钟有lse提供。 二、选择rtc唤醒与闹钟功能 内部参数介绍 闹钟配置 在配置时间时&#xff0c;注意将时间信息存储起来&#xff0c;防止复位后时间重新配置。 if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0)! 0x55AA)//判断标志位是否配置过&#xff0c;没有则进…