Loading......

文章背景图

算法 - 双指针专项 ①

2026-04-08
6
-
- 分钟
|

核心:有序数组 + 相向双指针,将暴力 O(n²)、O(n³) 复杂度优化到更高效的级别,是解决数组求和、计数、最接近值、三角形判定等问题的核心思路。


一、基础知识点(两数之和 II + 三数之和)

1. 两数之和 II - 输入有序数组(LeetCode 167)

题目描述

给定非递减有序数组与目标数 target,找出唯一两个数,使其和为 target,返回从 1 开始的下标。要求:常量级额外空间,不可重复使用元素。

核心思路

- 数组有序 → 用相向双指针(左→最小,右→最大)

- 双指针求和与 target 比较:

  • 和 = target → 找到答案

  • 和 > target → 右指针左移(缩小值)

  • 和 < target → 左指针右移(增大值)

复杂度

- 时间:O(n)

- 空间:O(1)

Python 实现

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        # 初始化双指针,左指针指向数组起始(最小元素),右指针指向数组末尾(最大元素)
        left = 0
        right = len(numbers) - 1
        # 双指针循环,左指针小于右指针避免重复使用同一元素
        while left < right:
            # 计算当前双指针指向元素的和
            s = numbers[left] + numbers[right]
            # 找到目标和,返回从1开始的下标(题目要求)
            if s == target:
                return [left + 1, right + 1]
            # 和大于目标值,右指针左移,缩小和的大小
            elif s > target:
                right -= 1
            # 和小于目标值,左指针右移,增大和的大小
            else:
                left += 1
        # 若没有找到符合条件的两个数,返回空列表(题目隐含无此情况,但保证代码完整性)
        return []

2. 三数之和(LeetCode 15)

题目描述

给定整数数组,找到所有不重复三元组 [a,b,c],满足 a+b+c=0,且下标互不相同。

核心思路

  • 1. 先排序:有序才能用双指针,同时方便去重

  • 2. 固定第一个数:枚举 nums[i],转化为在 i 右侧找两数之和 = -nums[i]

  • 3. 去重:枚举 i 时,与前一个数相同则跳过;找到三元组后,j、k 也跳过重复值

  • 4. 双指针找后两个数:左 j=i+1,右 = 末尾

关键优化

  • nums[i] + nums[i+1] + nums[i+2] > 0 → 后续全大于 0,直接 break

  • nums[i] + nums[-2] + nums[-1] < 0 → 当前 i 无解,continue

复杂度

- 时间:O(n²)(排序 O(nlogn) + 枚举 + 双指针 O(n²))

- 空间:O(1)(忽略排序栈空间)

Python 实现

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 排序:使数组有序,为双指针使用和去重提供前提
        nums.sort()
        # 存储最终结果的列表
        ans = []
        # 获取数组长度,用于循环边界控制
        n = len(nums)
        # 枚举第一个数(固定),遍历到n-2(保证后面有两个数组成三元组)
        for i in range(n - 2):
            x = nums[i]
            # 去重:当前数与前一个数相同,跳过,避免重复三元组
            if i > 0 and x == nums[i - 1]:
                continue
            # 优化1:最小的三个数之和已大于0,后续所有组合和都大于0,直接终止循环
            if x + nums[i + 1] + nums[i + 2] > 0:
                break
            # 优化2:当前数与最大的两个数之和仍小于0,当前数无法组成有效三元组,跳过
            if x + nums[-2] + nums[-1] < 0:
                continue
            # 初始化双指针,左指针在i+1(避免重复使用当前数),右指针在数组末尾
            j = i + 1
            k = n - 1
            # 双指针循环,左指针小于右指针避免重复使用元素
            while j < k:
                # 计算三元组的和
                s = x + nums[j] + nums[k]
                # 和大于0,右指针左移,缩小和的大小
                if s > 0:
                    k -= 1
                # 和小于0,左指针右移,增大和的大小
                elif s < 0:
                    j += 1
                # 和等于0,找到有效三元组
                else:
                    # 将有效三元组加入结果列表
                    ans.append([x, nums[j], nums[k]])
                    # 左指针右移,继续寻找其他可能的组合
                    j += 1
                    # 去重:左指针跳过重复元素
                    while j < k and nums[j] == nums[j - 1]:
                        j += 1
                    # 右指针左移,继续寻找其他可能的组合
                    k -= 1
                    # 去重:右指针跳过重复元素
                    while j < k and nums[k] == nums[k + 1]:
                        k -= 1
        # 返回所有不重复的有效三元组
        return ans

二、练习(4道经典双指针题目)

1. 2824. 统计和小于目标的下标对数目 🔢

题目描述

给定整数数组 nums 和整数 target,统计满足 0 <= i < j < n 且 nums[i] + nums[j] < target 的下标对数量。

示例

输入:nums = [-1,1,2,3,1], target = 2 输出:3 解释:总共有 3 个下标对满足题目描述:(0, 1)、(0, 2)、(0, 4),其两数之和分别为 0、1、0,均小于 target=2。

解题思路

  • 1. 数组排序:有序数组才能使用双指针快速统计

  • 2. 双指针规则:左指针 left 起始,右指针 right 末尾

  • - 两数之和 ≥ target → 右指针左移(缩小数值)

  • - 两数之和 < target → left 到 right-1 所有元素都能与 left 组成有效对,直接累加 right-left,左指针右移

复杂度

- 时间:O(nlogn)(排序)+O(n)(双指针)=O(nlogn)

- 空间:O(1)

Python 代码

class Solution:
    def countPairs(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        ans = 0
        nums.sort()
        
        while left < right:
            if nums[left] + nums[right] >= target:
                right -= 1
            else:
                ans += right - left
                left += 1
        return ans

2. 16. 最接近的三数之和 🎯

题目描述

给定数组和目标值,找出三个不同下标元素的和,使其与 target 最接近,返回该和。

示例

输入:nums = [-1,2,1,-4], target = 1 输出:2 解释:与 target 最接近的和是 2(-1 + 2 + 1 = 2),其与 target 的差值为 1,是所有可能三元组中和的最小差值。

解题思路

  • 1. 数组排序,初始化最接近和为无穷大

  • 2. 固定第一个数nums[i],双指针 left=i+1、right= 末尾找后两个数

  • 3. 计算三数之和,更新最接近 target 的结果

  • 4. 和 > target → 右指针左移;和 < target → 左指针右移

  • 5. 若和等于 target,直接返回(差值为 0,最优解)

复杂度

- 时间:O(n²)

- 空间:O(1)

Python 代码

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        ans = math.inf
        n = len(nums)
        nums.sort()
        
        for i in range(n-2):
            left = i + 1
            right = n - 1
            while left < right:
                s = nums[i] + nums[left] + nums[right]
                if abs(s - target) < abs(ans - target):
                    ans = s 
                if s == target:
                    return target
                elif s > target:
                    right -= 1
                else:
                    left += 1
        return ans

3. 18. 四数之和 🧩

题目描述

找出数组中不重复的四元组,满足四数之和等于 target,下标互不相同。

示例

输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]] 解释:三个四元组的和均为 0,且元素不重复,下标互不相同,符合题目要求。

解题思路

  • 1. 排序:去重 + 双指针必备

  • 2. 双重循环固定前两个数:i、j,并跳过重复元素

  • 3.双指针找后两个数:l=j+1、r= 末尾

  • 4. 四数之和 = target → 记录结果,跳过左右指针重复值

  • 5. 和偏小 → 左指针右移;和偏大 → 右指针左移

复杂度

- 时间:O(n³)

- 空间:O(1)

Python 代码

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        ans = []

        for i in range(n - 3):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, n - 2):
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                l, r = j + 1, n - 1
                while l < r:
                    s = nums[i] + nums[j] + nums[l] + nums[r]
                    if s == target:
                        ans.append([nums[i], nums[j], nums[l], nums[r]])
                        l += 1
                        r -= 1
                        while l < r and nums[l] == nums[l - 1]:
                            l += 1
                        while l < r and nums[r] == nums[r + 1]:
                            r -= 1
                    elif s < target:
                        l += 1
                    else:
                        r -= 1
        return ans

4. 611. 有效三角形的个数 📐

题目描述

给定非负整数数组,统计能组成三角形三条边的三元组个数。

三角形判定:两边之和 > 第三边(排序后只需满足最小两边之和 > 最大边)

示例

输入:nums = [2,2,3,4] 输出:3 解释:有效的组合是:(2,3,4)、(2,3,4)、(2,2,3),共 3 个三元组,均满足三角形判定条件。

三角形判定:两边之和 > 第三边(排序后只需满足最小两边之和 > 最大边)

解题思路

  • 1. 数组升序排序

  • 2. 固定最大边nums[k](从索引 2 开始遍历)

  • 3. 双指针 i=0、j=k-1 找前两条边

  • - nums[i]+nums[j] > nums[k] → i~j-1 所有元素都能与 j 组成有效三角形,累加 j-i,j 左移

  • - 不满足 → i 右移

复杂度

- 时间:O(n²)

- 空间:O(1)

Python 代码

class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        nums.sort()
        n = len(nums)
        ans = 0
        for k in range(2, n):
            c = nums[k]
            i = 0
            j = k - 1
            while i < j:
                if nums[i] + nums[j] > c:
                    ans += j - i 
                    j -= 1
                else:
                    i += 1
        return ans

三、双指针算法通用解题总结 ✨

核心适用场景

数组有序、需要找元素组合、求和、计数、求最接近值

统一解题步骤

  1. 排序数组(最关键前提)

  2. 固定部分元素(两数:不固定;三数:固定 1 个;四数:固定 2 个;三角形:固定最大边)

  3. 相向双指针遍历:左→小,右→大

  4. 根据和的大小移动指针(或根据判定条件调整指针)

  5. 去重处理(求不重复组合时必须跳过重复元素)

复杂度速记

  • 两数之和(有序):O(nlogn)

  • 三数之和 / 有效三角形:O(n²)

  • 四数之和:O(n³)

关键技巧

  • 统计数量时,直接累加区间数,不用逐个遍历(如 2824、611 题)

  • 求不重复解时,排序后跳过相同元素(如三数之和、四数之和)

  • 三角形问题:固定最大边,仅判断最小两边之和,简化判定条件

  • 多数之和(三数、四数):固定前 n-2 个数,转化为两数之和问题,降低复杂度

评论交流