问题描述
给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]],满足:
-
i != j != k -
nums[i] + nums[j] + nums[k] == 0
返回 所有和为 0 且不重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = [0,1,1]
输出:[]
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
提示:
-
3 <= nums.length <= 3000
-
-10^5 <= nums[i] <= 10^5
解题思路
-
排序 + 双指针
- 先对数组进行升序排序,这样方便去重,同时可以用双指针快速找到两数之和。
-
固定第一个数
- 遍历数组,固定
nums[i],剩下部分用双指针寻找两个数,使三数之和为 0。
- 遍历数组,固定
-
双指针
-
左指针
left = i + 1,右指针right = n - 1 -
计算
sum = nums[i] + nums[left] + nums[right]-
sum == 0→ 找到答案,记录并去重 -
sum < 0→ 左指针右移 -
sum > 0→ 右指针左移
-
-
-
去重技巧
-
遍历
i时跳过重复元素 -
左右指针移动时跳过重复元素
-
-
动态扩容结果数组
- 因为满足条件的三元组数量不确定,需要动态扩容结果数组,防止越界。
C语言代码实现
#include <stdlib.h>
#include <stdio.h>
// 数组排序函数
int cmp(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
*returnSize = 0;
if (numsSize < 3) {
*returnColumnSizes = NULL;
return NULL;
}
qsort(nums, numsSize, sizeof(int), cmp); // 排序
int capacity = 16; // 初始容量
int** result = malloc(sizeof(int*) * capacity);
*returnColumnSizes = malloc(sizeof(int) * capacity);
for (int i = 0; i < numsSize - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; // 去重
int left = i + 1;
int right = numsSize - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
if (*returnSize == capacity) { // 扩容
capacity *= 2;
result = realloc(result, sizeof(int*) * capacity);
*returnColumnSizes = realloc(*returnColumnSizes, sizeof(int) * capacity);
}
result[*returnSize] = malloc(sizeof(int) * 3);
result[*returnSize][0] = nums[i];
result[*returnSize][1] = nums[left];
result[*returnSize][2] = nums[right];
(*returnColumnSizes)[*returnSize] = 3;
(*returnSize)++;
while (left < right && nums[left] == nums[left + 1]) left++; // 去重
while (left < right && nums[right] == nums[right - 1]) right--; // 去重
left++;
right--;
}
else if (sum < 0) left++;
else right--;
}
}
return result;
}
算法复杂度分析
| 项目 | 复杂度 |
|---|---|
| 排序 | O(n log n) |
| 双指针遍历 | O(n²) |
| 总时间复杂度 | O(n²) |
| 空间复杂度 | O(1)(不算返回结果数组) |
核心总结
-
排序 → 便于去重和双指针搜索
-
双指针 → 高效寻找两数之和
-
去重 → 防止重复三元组
-
动态扩容 → 处理不确定的返回数组大小