核心:有序数组 + 相向双指针,将暴力 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 ans2. 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 ans3. 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 ans4. 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 个;三角形:固定最大边)
相向双指针遍历:左→小,右→大
根据和的大小移动指针(或根据判定条件调整指针)
去重处理(求不重复组合时必须跳过重复元素)
复杂度速记
两数之和(有序):O(nlogn)
三数之和 / 有效三角形:O(n²)
四数之和:O(n³)
关键技巧
统计数量时,直接累加区间数,不用逐个遍历(如 2824、611 题)
求不重复解时,排序后跳过相同元素(如三数之和、四数之和)
三角形问题:固定最大边,仅判断最小两边之和,简化判定条件
多数之和(三数、四数):固定前 n-2 个数,转化为两数之和问题,降低复杂度