给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]],满足:
i、j、k 互不相同nums[i] + nums[j] + nums[k] === 0请你返回所有和为 0 且不重复的三元组。
示例:
本题要求找出所有不重复的三元组,使三数之和等于 0。
直接三层循环会达到 O(n^3),效率太低。可以先对数组排序,固定第一个数 nums[i],再在右侧区间里用左右指针寻找另外两个数。
排序后,左右指针可以根据当前三数之和的大小决定移动方向:
left。right。所以这是一道典型的排序 + 双指针题。
排序后,固定一个下标 i,在区间 [i + 1, nums.length - 1] 中寻找另外两个数。
i:固定的第一个数下标。left:从 i + 1 开始,指向第二个数。right:从数组末尾开始,指向第三个数。ans:保存所有不重复的三元组。当前检查的三元组是:
先对 nums 升序排序。
对于每个固定下标 i:
nums[i] > 0,由于后面的数都大于等于 nums[i],三数之和一定大于 0,可以直接结束。i > 0 且 nums[i] === nums[i - 1],说明这个固定值已经处理过,跳过,避免重复答案。left = i + 1,right = nums.length - 1。在 left < right 时计算:
根据 sum 移动指针:
sum === 0:找到一个合法三元组,记录答案,然后同时移动 left 和 right,并跳过重复值。sum < 0:当前和太小,需要更大的数,left++。sum > 0:当前和太大,需要更小的数,right--。本题要求所有不重复三元组,所以只有当 sum === 0 时才更新答案。
记录答案后,必须继续移动两个指针寻找下一组结果:
然后跳过左右指针的重复值:
这样可以避免类似 [-1, 0, 1] 被重复加入。
边界条件:
nums.length < 3,不可能组成三元组,返回空数组。nums[i] > 0,后续不可能再得到和为 0 的三元组,可以提前结束。0,例如 [0, 0, 0, 0],只能返回一个 [0, 0, 0]。易错点:
sum 大小决定指针移动方向。i 时要去重:i > 0 && nums[i] === nums[i - 1]。left 或 right,否则可能漏掉合法组合。O(n^2)。排序需要 O(n log n),外层固定一个数,内层左右指针整体最多移动 O(n) 次,总体为 O(n^2)。O(1)。不计入返回结果数组,额外只使用了常数个变量;排序产生的栈空间取决于语言实现。