数组双指针技巧总结

raw/articles/2026/05/数组双指针技巧总结

一句话总结

系统讲解数组中的双指针技巧,分为快慢指针(原地修改、滑动窗口)和左右指针(二分查找、n数之和、反转、回文)两大类,配合 LeetCode 经典题目实践。

核心要点

1. 双指针两大分类

数组中的双指针技巧主要分为两类:

类型 指针移动方式 典型应用
快慢指针 同向而行,一快一慢 原地修改、滑动窗口
左右指针 相向而行或相背而行 二分查找、n数之和、反转、回文

2. 快慢指针:原地修改

核心思想:慢指针 slow 走在后面,快指针 fast 走在前面探路,找到符合条件的元素就赋值给 slow 并前进。

经典问题

LeetCode 问题 核心逻辑
26 删除有序数组中的重复项 fast 找到不重复元素→赋值给 nums[slow]slow++
27 移除元素 fast 遇到非 val 元素→赋值给 nums[slow]slow++
283 移动零 复用 #27 思路移除0,再将后半部分置0

通用模板

int slow = 0, fast = 0;
while (fast < nums.length) {
    if (符合条件) {
        nums[slow] = nums[fast];
        slow++;
    }
    fast++;
}
return slow; // 新长度

3. 快慢指针:滑动窗口

快慢指针形成窗口,通过扩大/缩小窗口解决问题。

通用框架

int left = 0, right = 0;
while (right < nums.size()) {
    window.addLast(nums[right]);
    right++;
    while (window needs shrink) {
        window.removeFirst(nums[left]);
        left++;
    }
}

4. 左右指针:相向移动

二分查找

int left = 0, right = nums.length - 1;
while (left <= right) {
    int mid = (right + left) / 2;
    if (nums[mid] == target) return mid;
    else if (nums[mid] < target) left = mid + 1;
    else right = mid - 1;
}

两数之和 II(有序数组)

int left = 0, right = numbers.length - 1;
while (left < right) {
    int sum = numbers[left] + numbers[right];
    if (sum == target) return new int[]{left + 1, right + 1};
    else if (sum < target) left++;
    else right--;
}

5. 左右指针:从中心扩散

反转数组/字符串

int left = 0, right = s.length - 1;
while (left < right) {
    swap(s[left], s[right]);
    left++; right--;
}

回文串判断

int left = 0, right = s.length() - 1;
while (left < right) {
    if (s.charAt(left) != s.charAt(right)) return false;
    left++; right--;
}
return true;

最长回文子串

for (int i = 0; i < s.length(); i++) {
    String s1 = palindrome(s, i, i);      // 奇数长度
    String s2 = palindrome(s, i, i + 1); // 偶数长度
    res = longer(res, s1, s2);
}

基本原理

快慢指针原理

初始:  [1, 1, 2, 2, 3]
        ↑    ↑
       slow fast

处理:  fast 找到不同元素 → 赋值给 slow → slow 前进

结果:  [1, 2, 3, 2, 3]
       slow (新长度=3)

左右指针原理

两数之和:
初始:  [2, 7, 11, 15], target=9
        ↑              ↑
       left          right
       sum=17 > 9, right--

调整:  [2, 7, 11, 15]
        ↑    ↑
       left right
       sum=9 == 9, 返回 [1, 2]

时间复杂度

操作 时间复杂度 说明
原地修改(去重/移除) O(N) 一次遍历,fast 指针扫描全部
滑动窗口 O(N) 均摊分析,每个元素进出窗口各一次
二分查找 O(log N) 每次缩小一半搜索范围
两数之和(有序) O(N) 最坏情况左右指针遍历整个数组
反转数组 O(N) 交换 n/2 次
最长回文子串 O(N²) 每个中心扩展,最坏情况 O(N²)

代码实现

关键代码片段

#26 删除有序数组中的重复项

class Solution {
    public int removeDuplicates(int[] nums) {
        if (nums.length == 0) return 0;
        int slow = 0, fast = 0;
        while (fast < nums.length) {
            if (nums[fast] != nums[slow]) {
                slow++;
                nums[slow] = nums[fast];
            }
            fast++;
        }
        return slow + 1;
    }
}

#27 移除元素

class Solution {
    public int removeElement(int[] nums, int val) {
        int fast = 0, slow = 0;
        while (fast < nums.length) {
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

#283 移动零(复用 #27)

class Solution {
    public void moveZeroes(int[] nums) {
        int p = removeElement(nums, 0);
        for (; p < nums.length; p++) {
            nums[p] = 0;
        }
    }

    int removeElement(int[] nums, int val) {
        int fast = 0, slow = 0;
        while (fast < nums.length) {
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

#167 两数之和 II

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left = 0, right = numbers.length - 1;
        while (left < right) {
            int sum = numbers[left] + numbers[right];
            if (sum == target) {
                return new int[]{left + 1, right + 1};
            } else if (sum < target) {
                left++;
            } else {
                right--;
            }
        }
        return new int[]{-1, -1};
    }
}

#5 最长回文子串

class Solution {
    public String longestPalindrome(String s) {
        String res = "";
        for (int i = 0; i < s.length(); i++) {
            String s1 = palindrome(s, i, i);      // 奇数长度
            String s2 = palindrome(s, i, i + 1); // 偶数长度
            res = res.length() > s1.length() ? res : s1;
            res = res.length() > s2.length() ? res : s2;
        }
        return res;
    }

    String palindrome(String s, int l, int r) {
        while (l >= 0 && r < s.length()
                && s.charAt(l) == s.charAt(r)) {
            l--; r++;
        }
        return s.substring(l + 1, r);
    }
}

实现要点

  • 快慢指针slow 记录有效位置,fast 探路;先判断再赋值(或先赋值再判断,取决于问题)
  • 左右指针相向:根据条件移动 leftright,注意循环条件 left < right
  • 回文中心扩散:考虑奇数/偶数两种中心情况;while 循环后返回 [l+1, r)

对比分析

维度 快慢指针 左右指针
指针方向 同向 相向或中心扩散
典型应用 原地修改、滑动窗口 二分、n数之和、反转、回文
时间复杂度 O(N) O(N) 或 O(log N)
空间复杂度 O(1) O(1)
适用结构 数组、链表 数组、字符串
维度 对向指针(左右) 中心扩散(左右)
指针起点 两端 中心
移动方向 向中间 向两端
典型问题 二分、两数之和 回文串

应用场景

  • 原地修改数组:去重、删除特定元素、移动零
  • 滑动窗口:最小覆盖子串、无重复字符最长子串、和≥target的最短子数组
  • 有序数组查找:二分查找、两数之和
  • 字符串处理:反转、回文判断、最长回文子串

验证方法

可通过以下 LeetCode 题目验证:

平台 题目 难度 对应技巧
LeetCode #26 删除有序数组中的重复项 Easy 快慢指针-原地修改
LeetCode #83 删除排序链表中的重复元素 Easy 快慢指针(链表版)
LeetCode #27 移除元素 Easy 快慢指针-原地修改
LeetCode #283 移动零 Easy 快慢指针-原地修改
LeetCode #167 两数之和 II Medium 左右指针-对向
LeetCode #344 反转字符串 Easy 左右指针-对向
LeetCode #5 最长回文子串 Medium 左右指针-中心扩散

相关概念

  • 双指针:本文的核心主题,系统讲解数组中的双指针技巧
  • 数组:双指针技巧的主要应用场景
  • 滑动窗口:快慢指针的重要应用形式
  • 二分查找:左右指针对向移动的典型应用
  • 递归:最长回文子串可视为递归中心扩展

相关实体

  • labuladong:本文作者,算法学习平台
  • LeetCode:本文涉及的题目来源平台

总结

数组双指针技巧通过将嵌套循环优化为线性扫描,将时间复杂度从 O(N²) 降低到 O(N)。快慢指针适用于原地修改和滑动窗口场景,左右指针适用于二分查找、n数之和、反转和回文问题。掌握这两大类技巧,可以解决大部分数组相关的算法问题。


Interactive Graph