【Day 7】公开打卡第7天 | LeetCode 15. 三数之和(排序 + 双指针 + 去重) + 常见去重技巧

3 阅读3分钟

正文结构

  1. 今日打卡宣言 Day 7,坚持第七天了!连续7天没断,已经形成小习惯。项目任务重,但这道三数之和是面试高频题,必须今天拿下。继续拒绝温水煮青蛙,冲!

  2. LeetCode 部分

    • 题目链接:leetcode.cn/problems/3s…
    • 难度:中等
    • 时间复杂度:O(n²)(排序 O(n log n) + 双指针 O(n))
    • 空间复杂度:O(1)(不考虑返回结果)
    • 核心思路(简短): 先对数组排序 → 固定第一个数 nums[i],然后在 [i+1, n-1] 区间用双指针(left、right)寻找两数之和等于 -nums[i]。遇到重复元素严格跳过,避免结果重复。
    • 代码(Go 版本,标准写法 + 详细去重注释):

Go

func threeSum(nums []int) [][]int {
    sort.Ints(nums)          // 必须排序
    n := len(nums)
    var res [][]int

    for i := 0; i < n-2; i++ {
        // 去重第一个数:跳过相同的 nums[i]
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        // 优化:nums[i] > 0 后面不可能有解
        if nums[i] > 0 {
            break
        }

        left, right := i+1, n-1
        for left < right {
            sum := nums[i] + nums[left] + nums[right]
            if sum == 0 {
                res = append(res, []int{nums[i], nums[left], nums[right]})

                // 去重 leftright
                for left < right && nums[left] == nums[left+1] {
                    left++
                }
                for left < right && nums[right] == nums[right-1] {
                    right--
                }
                left++
                right--
            } else if sum < 0 {
                left++
            } else {
                right--
            }
        }
    }
    return res
}
  • C++ 版本(如果你想放一个简洁双语版本):

C++

vector<vector<int>> threeSum(vector<int>& nums) {
    sort(nums.begin(), nums.end());
    vector<vector<int>> res;
    int n = nums.size();
    
    for (int i = 0; i < n - 2; i++)
    {
        if (nums[i] > 0) break;
        if (i > 0 && nums[i] == nums[i-1]) continue;
        
        int l = i + 1;
        int r = n - 1;
        while (l < r)
        {
            int sum = nums[i] + nums[l] + nums[r];
            
            if (sum == 0) {
                res.push_back({nums[i], nums[l], nums[r]});
                while(l < r && nums[l] == nums[l+1]) l++;
                while(l < r && nums[r] -- nums[r-1]) r--;
                l++;r--;
            } else if (sum < 0) {
                l++;
            } else {
                r--;
            }
        }
    }
    return res;
}
  • 易错点 & 优化点(面试最爱问):

    1. 必须先排序,否则双指针无法工作。

    2. 三处去重:i 去重 + left 去重 + right 去重(while 连续跳过相同元素)。

    3. if (nums[i] > 0) break; 是重要剪枝。

    4. 示例验证:

      • [-1,0,1,2,-1,-4] → [[-1,-1,2],[-1,0,1]]
      • [0,1,1] → []
      • [0,0,0] → [[0,0,0]]

进阶挑战, 假如是4数之和, 5数之和 。。。。。 K数之和呢?

使用递归➕双指针

  1. 知识点部分(今天重点讲去重) 排序 + 双指针去重通用技巧

    • 固定一个数后,剩下的变成“两数之和”问题 → 双指针从两端逼近。
    • 去重核心:每次找到答案后,用 while 跳过所有相同元素(避免 [ -1,-1,2 ] 重复出现)。
    • 适用场景:三数之和、四数之和、两数之和 II 等。
    • 面试追问:不排序怎么做?(用哈希表,但去重和时间复杂度更难处理,通常不推荐)。
  2. 今日感悟 这道题我之前刷过,但今天重新手写去重部分,才发现自己以前经常漏掉 left/right 的 while 去重,导致结果会有重复三元组。公开打卡逼着我必须把代码跑通所有样例,不能敷衍。Day 7 了,坚持一周的感觉真的不一样,斗志慢慢回来了!