面试官让我手写三数之和,我用了双指针,他露出了满意的微笑

22 阅读5分钟

从两数之和到四数相加,一套方法论搞定 N 数之和问题

前言

如果你刷过 LeetCode,一定对「两数之和」不陌生。但面试官往往会得寸进尺:「那三数之和呢?」「四数之和呢?」「四数相加 II 呢?」

今天,我就用两道经典题目,带你彻底搞懂 N 数之和问题的通用解法。

一、三数之和:排序 + 双指针

1.1 题目理解

给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j != k,且 nums[i] + nums[j] + nums[k] = 0。返回所有不重复的三元组。

示例:

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

1.2 暴力解法 vs 优化思路

最直观的想法是三重循环,时间复杂度 O(n³),面试官直接摇头。

核心优化思路:

  1. 先排序 → 让数组有序,便于去重和双指针移动
  2. 固定一个数 a,然后在剩余数组中用双指针b + c = -a

1.3 为什么不用哈希表?

学完「两数之和」后,你可能会想:用哈希表存 b,找 c = target - b 不行吗?

可行,但不优雅。三大痛点:

痛点说明
去重困难哈希表不处理顺序,[-1,0,1][-1,1,0] 可能被重复记录,需要额外 Set 去重
空间更大每固定一个 a 就要新建哈希表,空间 O(n) vs 双指针 O(1)
无法剪枝哈希表不依赖有序性,无法像双指针那样用 nums[i] > 0 提前终止

结论: 单数组 + 需要去重 → 排序 + 双指针是更优解。哈希表更适合「两数之和」和「四数相加 II」这类场景。

1.4 图解双指针过程

nums = [-4, -1, -1, 0, 1, 2] 为例:

排序后:[-4, -1, -1, 0, 1, 2]
         ↑    ↑              ↑
         i   left          right

固定 i = 0, a = -4
目标:找 b + c = 4
left 指向 -1right 指向 2,和为 1 < 4left++

1.5 完整代码逐行解析

var threeSum = function(nums) {
    const result = [];
    // 关键步骤1:排序
    nums.sort((a, b) => a - b);

    for (let i = 0; i < nums.length; i++) {
        // 剪枝:最小的数都大于0,三数之和不可能为0
        if (nums[i] > 0) return result;

        // 关键步骤2:对 a 去重
        if (i > 0 && nums[i] === nums[i - 1]) continue;

        let left = i + 1;
        let right = nums.length - 1;

        while (right > left) {
            const sum = nums[i] + nums[left] + nums[right];
            
            if (sum > 0) {
                right--;
            } else if (sum < 0) {
                left++;
            } else {
                result.push([nums[i], nums[left], nums[right]]);
                
                // 关键步骤3:对 b 和 c 去重
                while (right > left && nums[right] === nums[right - 1]) right--;
                while (right > left && nums[left] === nums[left + 1]) left++;
                
                right--;
                left++;
            }
        }
    }
    return result;
};

1.6 去重逻辑详解(面试必问)

去重位置代码原因
a 去重nums[i] === nums[i-1]同一个 a 值只处理一次
b 去重nums[left] === nums[left+1]找到解后,跳过相同的 b
c 去重nums[right] === nums[right-1]找到解后,跳过相同的 c

时间复杂度: O(n²)(排序 O(n log n) + 双指针 O(n²))
空间复杂度: O(log n) ~ O(n)(排序所需空间)


二、四数相加 II:分组 + 哈希表

2.1 题目理解

给你四个整数数组 nums1、nums2、nums3、nums4,长度都是 n。请你计算有多少个元组 (i, j, k, l) 满足: nums1[i] + nums2[j] + nums3[k] + nums4[l] = 0

示例:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2

2.2 思路转变

这题和三数之和最大的区别:四个数组相互独立,不需要考虑去重,只需要统计组合数量。

核心思想:

  • A + B + C + D = 0 转化为 A + B = -(C + D)
  • 用哈希表存储 A + B 的所有可能和及其出现次数
  • 遍历 C + D,查找 -(C + D) 在哈希表中的次数

2.3 代码实现

var fourSumCount = function(nums1, nums2, nums3, nums4) {
    const map = new Map();

    // 第一步:统计 nums1 + nums2 的所有可能和
    for (const a of nums1) {
        for (const b of nums2) {
            const sum = a + b;
            map.set(sum, (map.get(sum) || 0) + 1);
        }
    }

    let count = 0;

    // 第二步:在 nums3 + nums4 中找补数
    for (const c of nums3) {
        for (const d of nums4) {
            const target = -(c + d);
            if (map.has(target)) {
                count += map.get(target);
            }
        }
    }

    return count;
};

2.4 为什么这题用哈希表?

特性三数之和四数相加 II
数组数量1个数组4个独立数组
是否可排序可以可以(但没必要)
是否需要去重需要不需要
最优解法排序 + 双指针分组 + 哈希表

复杂度分析:

  • 时间复杂度:O(n²)(两个双重循环)
  • 空间复杂度:O(n²)(哈希表最多存 n² 个键值对)

三、方法论总结:N 数之和问题怎么破?

3.1 题目分类

题目类型特点推荐解法核心技巧
两数之和一个数组,找两数和为 target哈希表 O(n)空间换时间
三数之和一个数组,三数和为 0,去重排序 + 双指针 O(n²)排序创造有序性
四数之和一个数组,四数和为 target,去重排序 + 双指针 O(n³)三层循环 + 双指针
四数相加 II四个独立数组,不要求去重分组 + 哈希表 O(n²)转化为两数之和

3.2 核心技巧速记

// 1. 去重模板
if (i > 0 && nums[i] === nums[i-1]) continue;

// 2. 双指针收缩
while (left < right) {
    if (sum > target) right--;
    else if (sum < target) left++;
    else {
        while (left < right && nums[left] === nums[left+1]) left++;
        while (left < right && nums[right] === nums[right-1]) right--;
        left++;
        right--;
    }
}

// 3. 哈希表分组计数
const map = new Map();
for (...) {
    map.set(sum, (map.get(sum) || 0) + 1);
}

3.3 一句话记忆

单数组去重用双指针,多数组计数用哈希表。


结语

这两道题看似不同,实则体现了算法设计的两种重要思想:

  • 三数之和:用排序创造有序性,用双指针降低时间复杂度,用指针移动天然去重
  • 四数相加 II:用哈希表做分组映射,化四重循环为两重循环

下次面试遇到 N 数之和问题,你就可以自信地问面试官:

「请问是在一个数组里还是多个数组?需要去重吗?」

然后给出对应的最优解。


如果这篇文章对你有帮助,欢迎点赞、收藏、转发!