爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      算法魅力-双指针的实战

      首页 知识中心 软件开发 文章详情页

      算法魅力-双指针的实战

      2025-02-11 09:39:39 阅读次数:14

      cur,dest,元素,指针,数组

      1.双指针的介绍

      双指针算法是一种常用的算法技巧,通常用于解决数组或链表相关的题目。双指针算法的核心思想是使用两个指针在数组或链表上移动,这里的指针并不是只是指指针,我们可以用数组下标来代替,以达到解决问题的目的。根据具体问题,双指针可以分为以下几种类型:

       1. 左右指针(对撞指针)

      左右指针通常用于处理数组中的问题,其中一个指针从数组的开始位置向后移动,另一个指针从数组的结束位置向前移动。

      指针从两端向中间移动。一个指针从最左端开始,另一个从最右端开始,然后逐渐往中间逼
      近。
       
      对撞指针的 终止条件一般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:
      left == right (两个指针指向同一个位置)
      left > right (两个指针错开)

      典型问题:
      二分查找:在有序数组中查找一个特定的元素。
      两数之和:在数组中找到两个数,使它们的和等于一个给定的数。

       2. 快慢指针

      快慢指针主要用于处理链表中的问题,两个指针从同一位置出发,一个指针(快指针)每次移动两个节点,另一个指针(慢指针)每次移动一个节点。
       典型问题:
      判断链表中是否有环:Floyd 判圈算法(龟兔赛跑算法)。
      找到链表的中间节点:快指针到达终点时,慢指针正好在中间。


      双指针算法的关键在于指针的移动策略和终点的判断条件,根据具体问题,双指针的移动策略和判断条件可能会有所不同。

      2.题目练习讲解

       2.1 移动零

      283. 移动零 - 力扣(LeetCode)

       

      给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

      请注意 ,必须在不复制数组的情况下原地对数组进行操作。

      示例 1:

      输入: nums = [0,1,0,3,12]
      
      输出: [1,3,12,0,0]
      
      

      示例 2:

      输入: nums = [0]
      
      输出: [0]

       算法思路

      在本题中,我们可以用一个 cur 指针来扫描整个数组,另一个 dest 指针用来记录非零数序列
      的最后一个位置。根据 cur 在扫描的过程中,遇到的不同情况,分类处理,实现数组的划分。
      在 cur 遍历期间,使 [0, dest] 的元素全部都是非零元素, [dest + 1, cur - 1] 的元素全是零。
      [cur,n-1]是待处理的元素。
       
      首先,我们,我们让dest指向-1的位置,cur指向数组的首元素,通过循环控制cur的移动,无论cur指向谁,一次循环过后都要++,而当cur指向的数据是非0元素时,我们就让dest++,让加之后的dest与cur进行元素交换。
       
       
      详细就是:
       cur 依次往后遍历每个元素,遍历到的元素会有下面两种情况:
      i. 遇到的元素是 0 , cur 直接 ++ 。因为我们的目标是让 [dest + 1, cur - 1] 内
      的元素全都是零,因此当 cur 遇到 0 的时候,直接 ++ ,就可以让 0 在 cur - 1
      的位置上,从而在 [dest + 1, cur - 1] 内;
       
      ii. 遇到的元素不是 0 , dest++ ,并且交换 cur 位置和 dest 位置的元素,之后让
      cur++ ,扫描下⼀个元素。
      • 因为 dest 指向的位置是非零元素区间的最后一个位置,如果扫描到一个新的非零元
      素,那么它的位置应该在 dest + 1 的位置上,因此 dest 先自增 1 ;
      • dest++ 之后,指向的元素就是 0 元素(因为非零元素区间末尾的后一个元素就是
      0 ),因此可以交换到 cur 所处的位置上,实现 [0, dest] 的元素全部都是非零
      元素, [dest + 1, cur - 1] 的元素全是零。

       

      代码展示

      class Solution
      {
      public:
       void moveZeroes(vector<int>& nums) 
       {
       for(int cur = 0, dest = -1; cur < nums.size(); cur++)
       if(nums[cur]) // 处理⾮零元素
       swap(nums[++dest], nums[cur]);
       }
      };

       当然,我们也可以让dest指向首元素,后续算法逻辑类似,只是变成了先交换在++。

       

      class Solution
      {
      public:
       void moveZeroes(vector<int>& nums) 
       {
       for(int cur = 0, dest = 0; cur < nums.size(); cur++)
       if(nums[cur]) // 处理⾮零元素
       swap(nums[dest++], nums[cur]);
       }
      };

      画图效果效果

      算法魅力-双指针的实战

      2.2 复写零

      1089. 复写零 - 力扣(LeetCode)

      算法魅力-双指针的实战

       

      算法思路

      同样这道题我们用双指针的算法,我们首先普遍会想到定义两个指针,从前向后开始复写,从首元素开始移动,在移动过程中由于0会写两次,我们会发现后一个数据会被覆盖就会达不到效果。

      故当我们换成从后向前复写时,可以避免这种情况,但是我们需要找到复写的最后一个元素。

      我们还是定义两个指针:

      初始化两个指针 cur = 0 , dest = -1 ;
      b. 找到最后一个复写的数:
      i. 当 cur < n 的时候,一直执行下面循环:
      • 判断 cur 位置的元素:
      ◦ 如果是 0 的话, dest 往后移动两位;
      ◦ 否则, dest 往后移动一位。
      • 判断 dest 时候已经到结束位置,如果结束就终止循环;
      • 如果没有结束, cur++ ,继续判断。
       
       
      从 cur 位置开始往前遍历原数组,依次还原出复写后的结果数组:
      i. 判断 cur 位置的值:
      1. 如果是 0 : dest 以及 dest - 1 位置修改成 0 , dest -= 2 ;
      2. 如果⾮零: dest 位置修改成 0 , dest -= 1 ;
      ii. cur-- ,复写下一个位置。

       

      while(cur>=0){
              if(arr[cur])
              arr[dest--]=arr[cur--];
              else{
              arr[dest--]=0;
              arr[dest--]=0;
              cur--;
              }
      }

      我们发现有些情况会数组越界,超出一位,当我们往前遍历复写的时候就会出现问题,因为数组外赋值了。

      算法魅力-双指针的实战

      所以我们要处理数组越界的情况

       如果越界,n - 1 位置的值修改成 0 ;  cur 向移动一步; dest 向前移动两步。
       
       

       代码展示

      class Solution {
      public:
          void duplicateZeros(vector<int>& arr) {
             int cur=0,dest=-1;
             int n=arr.size();
             while(cur<n){
              if(arr[cur])
              dest++;
              else
              dest+=2;
              if(dest>=n-1)
              break;
              cur++;
             }
             if(dest==n){
              arr[n-1]=0;
              dest-=2;
              cur--;
             }
             while(cur>=0){
              if(arr[cur])
              arr[dest--]=arr[cur--];
              else{
              arr[dest--]=0;
              arr[dest--]=0;
              cur--;
              }
              
             }
          }
      };

      图片效果展示

      算法魅力-双指针的实战

       

      算法魅力-双指针的实战

       

      2.3 快乐数

      202. 快乐数 - 力扣(LeetCode)

      算法魅力-双指针的实战

       

      算法思路

      首先可以将题目解答猜成两部分,第一部分是求取每个位数上的平方和,第二步就是与1进行比较。

      通过快乐数的定义我们发现其实当它结果为1时,也会陷入一个1的循环。所以不管那种过程,累加次都会陷入循环,最终都会走到一个环中进行循环。

      情况一:一直在 1 中死循环,即 1 -> 1 -> 1 -> 1......
      情况二:在历史的数据中死循环,但始终变不到 1
       
      算法魅力-双指针的实战

      简单证明一下:

      经过一次变化之后的最大值 9^2 * 10 = 810 ( 2^31-1=2147483647 。选一个更大的最
      大 9999999999 ),也就是变化的区间在 [1, 810] 之间;  根据「鸽巢原理」,一个数变化 811 次之后,必然会形成一个循环;  因此,变化的过程最终会走到一个圈里面,因此可以用「快慢指针」来解决
       
       
      将「对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和」这一个
      操作记为 x 操作
       
      根据上述分析,我们可以知道,当重复执行 x 的时候,数据会陷入到一个「循环」之中。
      而「快慢指针」有一个特性,就是在一个圆圈中,快指针总是会追上慢指针的,也就是说他们总会
      相遇在一个位置上。如果相遇位置的值是 1 ,那么这个数一定是快乐数;如果相遇位置不是 1的话,那么就不是快乐数。

       

      代码展示

      class Solution {
      public:
          int ret(int n){
              int sum=0;
              while(n){
                 int a=n%10;
                 sum+=a*a;
                 n=n/10;
              }
              return sum;
          }
          bool isHappy(int n) {
             int slow=n;
             int fast=ret(n);
             while(slow!=fast){
              slow=ret(slow);
              fast=ret(ret(fast));
      
             }
             return slow==1;
          }
      };

       

      2.4 盛最多水的容器

      11. 盛最多水的容器 - 力扣(LeetCode)

       

      算法魅力-双指针的实战

      算法魅力-双指针的实战

      算法魅力-双指针的实战

      这道题我们首先会想到枚举,通过暴力的解法将每一种的体积算出来,选出最大的。

      lass Solution {
      public:
          int maxArea(vector<int>& height) {
             int n=height.size();
             int ret=0;
             for(int i=0;i<n;i++){
              for(int j=i+1;j<n;j++){
                  ret = max(ret, min(height[i], height[j]) * (j - i));
              }
             }
             return ret;
          }
      };

       但是这种解法会超时。所以需要换一种思路。我们可以采用对撞指针的思路。

      算法思路

      设两个指针 left , right 分别指向容器的左右两个端点,此时容器的容积 :
      v = (right - left) * min( height[right], height[left])
       
      容器的左边界为 height[left] ,右边界为 height[right] 。
      我们假设「左边边界」小于「右边边界」。
       
      如果此时我们固定一个边界,改变另一个边界,水的容积会有如下变化形式:
      容器的宽度一定变小。
       
      由于左边界较小,决定了水的高度。 如果改变左边界,新的水面高度度不确定,但是一定不会超过右边的柱子高度,因此容器的容积可能会增大。
      ◦
      如果改变右边界,无论右边界移动到哪里, 新的水面的高度一定不会超过左边界,也就是不会超过现在的水面高度,但是由于容器的宽度减小,因此容器的容器一定会变小的。
      由此可见,左边界和其余边界的组合情况都可以舍去。所以我们可以 left++ 跳过这个边界,继续去判断下一个左右边界。

       

      代码展示

      class Solution {
      public:
          int maxArea(vector<int>& height) {
              int left=0,right=height.size()-1;
              int max=0;
              for(int i=0;i<height.size();i++){
                  if(max<((right-left)*min(height[left],height[right])))
                  max=(right-left)*min(height[left],height[right]);
                  if(height[left]<=height[right])
                  left++;
                  else
                  right--;
              }
              return max;
          }
      };
      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.csdn.net/2302_79376097/article/details/142994454,作者:禁默,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:c语言回顾-结构体(2)

      下一篇:Java Swing 编程全面解析:从 AWT 到 Swing 的进化之路

      相关文章

      2025-05-19 09:04:14

      复杂度的OJ练习

      复杂度的OJ练习

      2025-05-19 09:04:14
      代码 , 复杂度 , 思路 , 数组 , 算法
      2025-05-16 09:15:24

      Redis Set集合

      Redis Set集合

      2025-05-16 09:15:24
      set , 个数 , 元素 , 示例 , 集合
      2025-05-16 09:15:24

      如何将一串数字用函数的方法倒过来(C语言)

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24
      函数 , 数字 , 数组
      2025-05-16 09:15:24

      jQuery遍历对象、数组、集合

      jQuery遍历对象、数组、集合

      2025-05-16 09:15:24
      jQuery , 对象 , 数组 , 遍历 , 集合
      2025-05-16 09:15:17

      递归,搜索,回溯算法(3)之穷举,暴搜,深搜,回溯,剪枝

      递归,搜索,回溯算法(3)之穷举,暴搜,深搜,回溯,剪枝

      2025-05-16 09:15:17
      回溯 , 子集 , 数组 , 算法 , 递归
      2025-05-14 10:33:31

      【数据结构】详细介绍串的简单模式匹配——朴素模式匹配算法

      【数据结构】详细介绍串的简单模式匹配——朴素模式匹配算法

      2025-05-14 10:33:31
      下标 , 元素 , 匹配 , 子串 , 模式匹配 , 算法
      2025-05-14 10:33:31

      计算机小白的成长历程——数组(1)

      计算机小白的成长历程——数组(1)

      2025-05-14 10:33:31
      strlen , 个数 , 元素 , 内存 , 十六进制 , 地址 , 数组
      2025-05-14 10:33:31

      计算机小白的成长历程——习题演练(函数篇)

      计算机小白的成长历程——习题演练(函数篇)

      2025-05-14 10:33:31
      函数 , 字符串 , 数组 , 知识点 , 编写 , 迭代 , 递归
      2025-05-14 10:33:16

      C++ 11新特性之unique_ptr

      在C++ 11标准中,智能指针作为一种强大的资源管理工具被引入,极大地提升了代码的健壮性和安全性。其中,std::unique_ptr作为唯一所有权智能指针,以其独特的非拷贝特性及自动内存释放机制,成为现代C++编程中的重要组件。

      2025-05-14 10:33:16
      ptr , std , unique , 指向 , 指针 , 赋值
      2025-05-14 10:33:16

      30天拿下Rust之向量

      在Rust语言中,向量(Vector)是一种动态数组类型,可以存储相同类型的元素,并且可以在运行时改变大小。向量是Rust标准库中的一部分,位于std::vec模块中。

      2025-05-14 10:33:16
      Rust , 使用 , 元素 , 向量 , 方法 , 索引 , 迭代
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5233252

      查看更多

      最新文章

      复杂度的OJ练习

      2025-05-19 09:04:14

      Redis Set集合

      2025-05-16 09:15:24

      如何将一串数字用函数的方法倒过来(C语言)

      2025-05-16 09:15:24

      【数据结构】详细介绍串的简单模式匹配——朴素模式匹配算法

      2025-05-14 10:33:31

      C++ 11新特性之unique_ptr

      2025-05-14 10:33:16

      30天拿下Rust之向量

      2025-05-14 10:33:16

      查看更多

      热门文章

      Arrays类的使用

      2023-06-08 06:23:00

      python学习(6)——列表元素的添加、删除、修改及排序

      2023-05-22 03:00:29

      C语言结构体与结构体指针的使用

      2023-03-08 10:38:36

      C++入门篇之C++ 指针

      2023-03-14 11:26:53

      指针(*)、取地址(&)、解引用(*)与引用(&)

      2023-04-10 08:54:19

      Python打乱列表/数组原顺序,新列表/数组中元素随机分布

      2023-04-13 09:36:44

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      【C++ 差分数组 前后缀分解】P7404家庭菜园

      【数据结构】详细介绍串的简单模式匹配——朴素模式匹配算法

      元素与集合的问题思考

      用go语言,给定一个下标从1开始,包含不同整数的数组 nums,数组长度为 n。

      【C语言】带你玩转数组(全程高能)

      用go语言,给定一个数组arr,长度为n,表示n个格子的分数,并且这些格子首尾相连, 孩子不能选相邻的格子,不能回头选,不能选超过一圈

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号