跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

双指针算法详解:原理与经典题目实战

系统讲解双指针算法,涵盖对撞指针与快慢指针两种核心形式。通过移动零、复写零、快乐数、盛最多水的容器、有效三角形个数、两数之和、三数之和及四数之和八道经典题目,演示如何从暴力解法优化至双指针方案。文章提供 C++ 代码实现,涵盖数组操作、去重、溢出处理等关键细节,帮助读者掌握双指针在解决序列问题中的核心应用。

DebugKing发布于 2026/3/27更新于 2026/9/962 浏览
双指针算法详解:原理与经典题目实战

1. 双指针算法

在数据结构链表和顺序表的学习中,我们已经使用过双指针的算法思想。例如删除数组中的重复元素、判断链表是否为环、找出链表的中间节点等。

常见的双指针有两种形式,一种是对撞指针,一种是左右指针。

  • 对撞指针:一般用于顺序结构中。一个指针从最左端开始,另一个从最右端开始,逐渐往中间逼近。终止条件一般是两个指针相遇(left == right)或者错开(left > right)。
  • 快慢指针:又称为龟兔赛跑算法。基本思想是使用两个移动速度不同的指针在数组或链表等序列结构上移动。最常用的方式是在一次循环中,每次让慢的指针向后移动一位,而快的指针往后移动两位。这种方法对于处理环形链表或数组非常有用。

2. 双指针在算法题内的使用

2.1 移动零

283. 移动零 - LeetCode

题目解析

将数组内的所有零都移动到非零元素之后,并且保持所有非零元素的相对位置不变。 示例:输入 [1,0,3,12],输出 [1,3,12,0,0]。

算法原理讲解

该题属于数组分块类型,特征是将数组划分为几个区间。适合使用双指针解决。在数组中使用双指针时,指针通常用数组下标表示。

思路: 创建两个数组下标 cur 和 dest。cur 从左往右遍历数组,dest 指向已处理区间内的最后一个非零元素。数组可划分为三个区间:[0, dest](非零)、[dest+1, src-1](零)、[src, n-1](待遍历)。最终当 src 遍历到末尾时,剩余部分补零。

流程:

  1. 当 cur 位置元素为 0 时,只让 cur++。
  2. 当 cur 位置元素不为 0 时,先让 dest++,再交换 dest 和 cur 位置的元素。
代码实现
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int dest = -1;
        for(int src = 0; src < nums.size(); src++) {
            if(nums[src] != 0 && ++dest != src) {
                swap(nums[src], nums[dest]);
            }
        }
    }
};

2.2 复写零

1089. 复写零 - LeetCode

题目解析

将数组中的每个 0 复写一遍,非 0 元素保持不变。要求就地实现,不能申请新内存空间。

算法原理讲解

正向遍历时,若直接复写会导致后续元素被覆盖。因此采用从后向前遍历的策略。

步骤:

  1. 初始化 cur = 0, dest = -1。
  2. 找到最后一个需要复写的数:遍历原数组,遇到 0 则 dest 移两位,否则移一位。直到 dest 越界或到达末尾。
  3. 处理特殊情况:如果最后一个复写的数是 0 且导致 dest 越界,需单独处理边界。
  4. 从 cur 位置开始往前遍历,依次还原复写后的结果。
代码实现
class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur = 0, dest = -1;
        while(cur < arr.size()) {
            if(arr[cur] == 0) dest += 2;
            else dest++;
            if(dest >= arr.size() - 1) break;
            cur++;
        }
        if(dest == arr.size()) {
            arr[arr.size() - 1] = 0;
            cur--;
            dest -= 2;
        }
        while(cur >= 0) {
            if(arr[cur] == 0) {
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            } else {
                arr[dest--] = arr[cur--];
            }
        }
    }
};

2.3 快乐数

202. 快乐数 - LeetCode

题目解析

判断给定整数是否是快乐数。快乐数定义为不断替换成其各位数字平方之和,最后循环结果为 1。

算法原理讲解

使用快慢指针检测循环。快指针每次走两步,慢指针每次走一步。若相遇且值为 1,则是快乐数;否则不是。

代码实现
class Solution {
public:
    int getsum(int n) {
        int sum = 0;
        while(n > 0) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int fast = getsum(getsum(n));
        int slow = getsum(n);
        while(slow != fast) {
            fast = getsum(getsum(fast));
            slow = getsum(slow);
        }
        return slow == 1;
    }
};

2.4 盛水最多的容器

11. 盛最多水的容器 - LeetCode

题目解析

找出能盛最多水的容器。容积 = 两边距离 × 短边高度。

算法原理讲解

暴力解法时间复杂度 O(N^2)。优化方案使用双指针,初始分别指向首尾。每次移动较短的一边,因为移动较长边只会减小宽度且高度不会增加,容积必然减小。

代码实现
class Solution {
public:
    int maxArea(vector<int>& height) {
        int left = 0, right = height.size() - 1, sum = 0;
        while(left < right) {
            int h = min(height[left], height[right]);
            int len = right - left;
            sum = max(h * len, sum);
            if(height[left] < height[right]) left++;
            else right--;
        }
        return sum;
    }
};

2.5 有效三角形的个数

611. 有效三角形个数 - LeetCode

题目解析

统计数组中能组成三角形的三元组个数。任意两边之和大于第三边。

算法原理讲解

暴力枚举 O(N^3)。优化方法:先排序,固定最大边 i,使用双指针 left 和 right 寻找另外两边。若 nums[left] + nums[right] > nums[i],则 left 到 right-1 的所有元素均满足条件。

代码实现
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size(), sum = 0;
        for(int i = 2; i < n; i++) {
            int left = 0, right = i - 1;
            while(left < right) {
                if(nums[left] + nums[right] > nums[i]) {
                    sum += right - left;
                    right--;
                } else {
                    left++;
                }
            }
        }
        return sum;
    }
};

2.6 和为 s 的两个数字

LCR 179. 查找总价格为目标值的两个商品 - LeetCode

题目解析

在升序数组中找出两数之和等于 target 的二元组。

算法原理讲解

使用双指针 left 和 right 分别指向首尾。根据当前和与 target 的大小关系移动指针。时间复杂度 O(N)。

代码实现
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        int n = price.size();
        int left = 0, right = n - 1;
        while(left < right) {
            if(price[left] + price[right] > target) right--;
            else if(price[left] + price[right] < target) left++;
            else return {price[left], price[right]};
        }
        return {-1, -1};
    }
};

2.7 三数之和

15. 三数之和 - LeetCode

题目解析

找出所有和为 0 且不重复的三元组。

算法原理讲解

先排序,固定一个数 a,转化为两数之和问题。使用双指针在剩余区间查找。注意去重:跳过相同的 a、left 和 right。

代码实现
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> v;
        int n = nums.size();
        for(int i = 0; i < n - 2;) {
            if(nums[i] > 0) break;
            int left = i + 1, right = n - 1, sum = -nums[i];
            while(left < right) {
                if(nums[left] + nums[right] > sum) right--;
                else if(nums[left] + nums[right] < sum) left++;
                else {
                    v.push_back({nums[left], nums[right], nums[i]});
                    left++, right--;
                    while(left < right && nums[left] == nums[left - 1]) left++;
                    while(left < right && nums[right] == nums[right + 1]) right--;
                }
            }
            i++;
            while(i < n && nums[i] == nums[i - 1]) i++;
        }
        return v;
    }
};

2.8 四数之和

18. 四数之和 - LeetCode

题目解析

找出所有和为 target 且不重复的四元组。

算法原理讲解

类似三数之和,固定两个数,剩余部分使用双指针。注意数据类型溢出问题,求和时使用 long long。

代码实现
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> v;
        int n = nums.size();
        for(int i = 0; i < n;) {
            for(int j = i + 1; j < n;) {
                long long count2 = (long long)target - nums[i] - nums[j];
                int left = j + 1, right = n - 1;
                while(left < right) {
                    int count1 = nums[left] + nums[right];
                    if(count1 > count2) right--;
                    else if(count1 < count2) left++;
                    else {
                        v.push_back({nums[left], nums[right], nums[i], nums[j]});
                        left++, right--;
                        while(left < right && nums[left] == nums[left - 1]) left++;
                        while(left < right && nums[right] == nums[right + 1]) right--;
                    }
                }
                j++;
                while(j < n && nums[j] == nums[j - 1]) j++;
            }
            i++;
            while(i < n && nums[i] == nums[i - 1]) i++;
        }
        return v;
    }
};

目录

  1. 1. 双指针算法
  2. 2. 双指针在算法题内的使用
  3. 2.1 移动零
  4. 题目解析
  5. 算法原理讲解
  6. 代码实现
  7. 2.2 复写零
  8. 题目解析
  9. 算法原理讲解
  10. 代码实现
  11. 2.3 快乐数
  12. 题目解析
  13. 算法原理讲解
  14. 代码实现
  15. 2.4 盛水最多的容器
  16. 题目解析
  17. 算法原理讲解
  18. 代码实现
  19. 2.5 有效三角形的个数
  20. 题目解析
  21. 算法原理讲解
  22. 代码实现
  23. 2.6 和为 s 的两个数字
  24. 题目解析
  25. 算法原理讲解
  26. 代码实现
  27. 2.7 三数之和
  28. 题目解析
  29. 算法原理讲解
  30. 代码实现
  31. 2.8 四数之和
  32. 题目解析
  33. 算法原理讲解
  34. 代码实现

更多推荐文章

查看全部
  • MySQL 表约束详解:空值、主键与外键
  • Axure 制作 AI 自动对话机器人原型教程
  • 磁盘到 inode:深入理解 Linux ext 文件系统底层原理
  • Python 异步爬虫与 K8S 弹性伸缩:构建高并发数据采集引擎
  • 基于 WebRTC 与 AI 接口的实时语音对话系统构建
  • Win10 系统关闭 Microsoft Copilot 弹窗的 6 种有效方案
  • Node.js 24 LTS 正式发布,稳定支持到 2028 年
  • OpenClaw Linux 部署教程
  • 网络通讯核心协议详解:TCP、UDP 与 HTTP/HTTPS
  • Trae AI 将设计稿自动生成前端代码实战指南
  • 随机森林算法原理与 Python 实战代码
  • 使用 Vue.js 构建 Java 桌面应用
  • SQL 自动生成 ER 图与数据库设计基础
  • ChatGPT 保护指令:提升 GPTs 提示词与知识库安全性
  • 双指针算法详解与经典题目实战
  • 基于 Comsol 的 Ar 棒板粗通道流注放电仿真分析
  • C++ AVL 树功能实现原理剖析
  • 数据结构:单链表的头插/尾插及头删/尾删操作
  • Unity VR Pico 开发环境一键配置手册
  • 大模型训练数据白皮书发布:大模型是数据要素价值释放的最短路径

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online