【双指针专题】第八讲:四数之和

🌿 从一数到二数、三数,再到四数,双指针思维的进阶之路至此完整。

🌱 文章摘要

这是《双指针专题》最后一篇
“移动零”开始,我们经历了划分、复写、快慢、对撞、计数、查找与三数之和,逐步走完了双指针的全景路线。
而这一讲《四数之和》,将把所有技巧再向前推进一层:

  1. 固定两位,
  2. 双指针收缩查找 pair,
  3. 结合去重策略

高效地在 O(n³) 时间内找到所有和为 target 的四元组。

这是多数之和问题的“完全体”,也是滑动窗口专题开启前最有含金量的一篇。


🧭 导读

欢迎来到《双指针算法专题》第八篇,也是本系列的收官之作 🎉

《四数之和》的题目是“三数之和”的自然进阶:

  • 给定一个数组和目标值 target,

  • 找出所有不重复的四元组,使它们的和等于 target。

最直接的做法是四重循环 O(n⁴),但我们可以通过

  1. 排序
  2. 固定两位数 i、j
  3. 剩余区间用双指针查找 pair

将复杂度降为 O(n³),且逻辑十分清晰:

这道题的核心不是“技巧翻新”,而是把你在“三数之和”中学到的内容再 抽象一层,并做好去重细节的处理。


  🧭 本系列目录

📚 建议收藏此目录,方便跳转阅读

【双指针专题】第一讲:移动零-优快云博客

【双指针专题】第二讲:复写零-优快云博客

【双指针专题】第三讲:快乐数-优快云博客

【双指针专题】第四讲:盛最多水的容器-优快云博客

【双指针专题】第五讲:有效三角形的个数-优快云博客

【双指针专题】第六讲:查找总价格为目标值的两个商品-优快云博客

【双指针专题】第七讲:三数之和-优快云博客

【双指针专题】第八讲:四数之和-优快云博客


一、题目描述

给你一个包含 n 个整数的数组 nums 和一个目标值 target,请你找出所有 不重复的四元组 [a, b, c, d],使得:

a + b + c + d = target

返回这些四元组列表

注意:

  •  四个下标必须互不相同;
  • 答案中不能有重复的四元组;
  • 结果顺序不重要。 

示例:

输入: nums = [1, 0, -1, 0, -2, 2], target = 0
输出: [ [-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1] ]

二、思路引导:固定两位 + 双指针查找

如果用最直接的暴力法,四重循环枚举所有 a、b、c、d,时间复杂度 O(n⁴),显然会超时。

所以我们要换个角度:

  • 三数之和可以通过「固定一位 + 双指针」来优化;

  • 四数之和自然也可以「固定两位 + 双指针」来降低复杂度。

步骤如下:

  1. 排序 数组,为去重和双指针做准备;

  2. 固定 i → 第一位数字;

  3. 固定 j → 第二位数字;

  4. [j+1, n-1] 区间用 双指针 夹逼查找剩下两数;

  5. sum 小于目标 → 左指针右移;
    sum 大于目标 → 右指针左移;
    sum 恰好等于目标 → 加入答案,并跳过重复。

这个过程本质就是三数之和再多套一层循环,思路完全一致,只是去重和边界处理要更细。


三、算法流程

步骤 1:排序

对数组进行排序,便于去重和使用双指针。

步骤 2:外层循环固定 i

遍历 i ∈ [0, n-4],跳过重复的 nums[i]。

步骤 3:内层循环固定 j

遍历 j ∈ [i+1, n-3],跳过重复的 nums[j]。

步骤 4:双指针查找

left = j + 1right = n - 1,目标为 remain = target - nums[i] - nums[j]

  • 若 sum < remain → left++

  • 若 sum > remain → right--

  • 若相等 → 加入答案,left++、right--,并跳过重复值。

步骤 5:遍历结束

返回所有找到的四元组。


四、代码实现(C++)

#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        int n = nums.size();
        if (n < 4) return res;

        sort(nums.begin(), nums.end());

        for (int i = 0; i < n - 3; i++) {
            // 固定第一位,跳过重复
            if (i > 0 && nums[i] == nums[i - 1]) continue;

            for (int j = i + 1; j < n - 2; j++) {
                // 固定第二位,跳过重复
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;

                int left = j + 1, right = n - 1;
                long long remain = (long long)target - nums[i] - nums[j];

                while (left < right) {
                    long long sum = (long long)nums[left] + nums[right];
                    if (sum < remain) left++;
                    else if (sum > remain) right--;
                    else {
                        res.push_back({nums[i], nums[j], nums[left], nums[right]});
                        left++; right--;
                        while (left < right && nums[left] == nums[left - 1]) left++;
                        while (left < right && nums[right] == nums[right + 1]) right--;
                    }
                }
            }
        }
        return res;
    }
};

五、代码剖析与细节问题

1. 去重是关键

  • i、j 外层循环跳过相邻重复值,是为了避免生成相同开头的四元组。

  • 内层双指针在找到一个四元组后,也必须跳过重复的 left / right,否则会出现重复解。
    如果忘记这一步,几乎一定会出错,是这题最大的坑之一。

2. long long 防溢出

因为 target 与 nums[i]、nums[j] 可能都较大,计算 remain 和 sum 时用 long long 防止 int 溢出导致逻辑错误。

3. 循环边界要注意

  • i 最大取到 n - 4
  • j 最大取到 n - 3
  • left = j + 1, right = n - 1
  • 这些边界如果写错,很容易漏掉合法组合。

六、复杂度分析

  • 时间复杂度:O(n³)

两重循环 O(n²) + 双指针 O(n),合计 O(n³)。

  • 空间复杂度:O(1)

七、总结

“四数之和”几乎没有新的技巧,更多是对“三数之和”思路的 延展与细化
这类题的本质就是:

「固定若干位 → 双指针 → 去重」
再按层数递进处理。

这也是 K 数之和问题的通用模板


🏁 收官寄语

至此,我们的“双指针”专题正式收官。从“移动零”到“四数之和”,你已经经历了

  • 前向扫描
  • 反向复写
  • 快慢追击
  • 区间对撞
  • 计数统计
  • 多数之和

这一整套逻辑思维的搭建,是很多人从“会写题”迈向“理解题”的分水岭。

接下来,我们将正式进入下一个专题——
👉 滑动窗口
👉 聚焦子区间问题与动态维护,
👉 这是算法进阶的又一条主线 🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值