【算法】15. 三数之和
一、题目是啥?一句话说清
给你一个整数数组,找出所有不重复的三元组(三个数),使得这三个数的和等于零。
示例:
- 输入:nums = [-1, 0, 1, 2, -1, -4]
- 输出:[[-1, -1, 2], [-1, 0, 1]]
二、解题核心
先对数组排序,然后固定一个数,用双指针在剩余数组中寻找两个数,使得三数之和为零。同时,通过跳过重复值来避免重复三元组。
这就像你请朋友吃饭,要点三个菜,总价正好是100元。你先固定一个主菜的价格,然后用两个指针在菜单上滑动,找两个配菜的价格,使得三个菜的总价是100元。如果遇到重复的菜价,就跳过以免点重样的菜。
三、关键在哪里?(3个核心点)
想理解并解决这道题,必须抓住以下三个关键点:
1. 排序数组
- 是什么:首先将数组从小到大排序。
- 为什么重要:排序后,数组变得有序,我们可以使用双指针来高效地寻找两个数的和,同时容易跳过重复值,避免重复三元组。
2. 双指针技巧
- 是什么:对于每个固定的数
nums[i]
,使用左指针(从i+1
开始)和右指针(从数组末尾开始)来寻找两个数,使得它们的和等于-nums[i]
。 - 为什么重要:双指针可以将两数之和的时间复杂度从 O(n²) 降低到 O(n),从而将整体算法优化到 O(n²)。
3. 跳过重复值
- 是什么:在固定数、移动左指针和右指针时,如果遇到重复值,就跳过它们。
- 为什么重要:这是避免重复三元组的关键。如果不跳过重复值,可能会产生多个相同的三元组。
四、看图理解流程(通俗理解版本)
让我们用 nums = [-1, 0, 1, 2, -1, -4] 的例子来可视化过程:
-
排序数组:先排序,得到 [-4, -1, -1, 0, 1, 2]。
-
固定第一个数(-4):
- 我们需要找两个数,它们的和等于 4(因为 -(-4) = 4)。
- 左指针在 -1,右指针在 2。
- 计算和:-1 + 2 = 1 < 4,左指针右移(到下一个 -1)。
- 计算和:-1 + 2 = 1 < 4,左指针右移(到 0)。
- 计算和:0 + 2 = 2 < 4,左指针右移(到 1)。
- 计算和:1 + 2 = 3 < 4,左指针右移,但左指针超过右指针,停止。没有找到三元组。
-
固定第二个数(-1):
- 注意:跳过重复值!第一个固定数是 -4,现在固定数是 -1,但 since 我们已经处理过 -1,所以这里固定第一个 -1(索引1)。
- 我们需要找两个数,它们的和等于 1(因为 -(-1) = 1)。
- 左指针在下一个 -1(索引2),右指针在 2。
- 计算和:-1 + 2 = 1 == 1,找到三元组 [-1, -1, 2]。
- 左指针右移(到 0),右指针左移(到 1)。
- 计算和:0 + 1 = 1 == 1,找到三元组 [-1, 0, 1]。
- 左指针右移,右指针左移,停止。
-
固定第三个数(-1):
- 但这是第二个 -1,与上一个固定数相同,所以跳过(避免重复)。
-
固定第四个数(0):
- 我们需要找两个数,它们的和等于 0。
- 左指针在 1,右指针在 2。
- 计算和:1 + 2 = 3 > 0,右指针左移(到 1)。
- 但左指针和右指针相遇,停止。没有新三元组。
-
结束:最终得到两个三元组 [-1, -1, 2] 和 [-1, 0, 1]。
五、C++ 代码实现(附详细注释)
#include <iostream>
#include <vector>
#include <algorithm>
using