题目链接: 三数之和
有关题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c
使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含**重复**的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-10^5 <= nums[i] <= 10^5
题解
我们可以在O(N^3)三重循环下把结果算出来,但是时间复杂度太大了
不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:
第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;
第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。
也就是说,我们枚举的三元组 (a, b, c) ,a≤b≤c,保证了只有 (a, b, c)(a,b,c) 这个顺序会被枚举到,而 (b, a, c)(b,a,c)、(c, b, a)(c,b,a) 等等这些不会,这样就减少了重复。
同时我们还要保证相邻的两个元素,不能重复
第一重循环,时间复杂度为 O(N)*O(N),因此枚举的总时间复杂度为 O(N^2)。
由于排序的时间复杂度为 O(NlogN),在渐进意义下小于前者
因此算法的总时间复杂度为 O(N^2)
2
解题思路
1、判断输入数组为空、或元素个数小于3个,直接返回NULL
2、将输入数组升序排序
3、3.1、判断当前元素是否为正,是则结束循环,因为后续的数字也都为正,不会出现三数之和为0的情况
3.2、判断当前元素是否与上一次相等,相等则跳过进行去重
3.3、从 i + 1 到 numsSize - 1 进行左右指针判断三数之和sum:
3.3.1、sum == 0 :
1、找到一组值,进行保存
2、对left、right进行去重
3.3.2、sum < 0 :
left++
3.3.3、sum > 0
right--
其中参数 int** returnColumnSizes 表示返回数组中每一行的列数:
分配:
*returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));
使用:
(*returnColumnSizes)[*returnSize] = 3; /* 返回数组当前行的列数为3 */
其中返回数组ret为类似二维数组:
分配:
int** ret = (int**)malloc(numsSize * numsSize * sizeof(int*));
ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
使用:
ret[*returnSize][0] = nums[i];
/*
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
//qsort的比较函数
int cmp_int (const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int**returnColumnSizes){
/* 先记录返回的行数为0 */
*returnSize = 0;
/* 输入为空、或元素个数小于3则返回NULL */
if(nums == NULL || numsSize < 3)
return NULL;
int first = 0,second = 0;
/* 将nums排序为升序排列 */
qsort(nums,numsSize,sizeof(int),cmp_int);
/* 分配返回数组、返回数组的列数 */
int** ret = (int**)malloc( sizeof(int*) * numsSize* numsSize);//<--这个地方中的开辟空间?//sizeof(int*) * numsSize * numsSize????
*returnColumnSizes = (int*)malloc(numsSize* numsSize * sizeof(int));//返回数组的行数
for (first = 0; first < numsSize; first++)
{
//第一个数字大于0直接break
if (nums[first] > 0)
break;
//保证和上一次枚举的数字不一样
if (first > 0 && nums[first - 1] == nums[first] )
//这边就因为 first 写到了后面,就在力扣出现了非法访问内存区域的
//提示,浪费了大量时间,要细心啊!!!
continue;
int third = numsSize - 1;//指向数组的最右端
int second = first + 1;
while(second < third)
{
int sum = nums[first] + nums[second] + nums[third];
if ( sum == 0 )
{
ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
ret[*returnSize][0] = nums[first];
ret[*returnSize][1] = nums[second];
ret[*returnSize][2] = nums[third];
/* 返回数组当前行的列数为3 */
(*returnColumnSizes)[*returnSize] = 3;
//行数加1
(*returnSize)++;
//对指向b,c的指针进行去重操作
while(second < third && nums[second] == nums[++second]);
while(second < third && nums[third] == nums[--third]);
}
else if (sum < 0)
second++;
else
third--;
}
}
return ret;
}
这两种解法大致都一个意思
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
//当然我们这里可能开辟的数组空间大小太大了,所以我么可以根据numsSize的大小
//来开辟空间
#define nums_max_length 3000
//快排的比较函数
int cmp_by_int (const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize = 0;//返回个数先置零
//3个以下元素或者无元素排除
if (numsSize < 3 || nums == NULL)
return NULL;
//按照上面题干给的要求动态开辟返回数组和返回数组每一行的列数
int** ret = (int**)malloc(sizeof(int*) * nums_max_length *nums_max_length);
*returnColumnSizes = (int*)malloc(sizeof(int) * nums_max_length * nums_max_length);
//升序排序
qsort(nums,numsSize,sizeof(int),cmp_by_int);
int i = 0;
for (i = 0; i < numsSize; i++)
{
//第一个元素为真又由于升序,直接排除
if (nums[i] > 0)
break;
//由于不能重复,保证相邻两个元素不相等
//去重
if (i > 0 && nums[i] == nums[i - 1])
continue;
int j = i + 1;//左指针
int k = numsSize - 1;//右指针
while(j < k)
{
int sum = nums[i] + nums[j] + nums[k];//三数之和
//判断sum与0的关系
if(sum == 0)
{
//记录三数
ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
ret[*returnSize][0] = nums[i];
ret[*returnSize][1] = nums[j];
ret[*returnSize][2] = nums[k];
//记录当前数组的列数
(*returnColumnSizes)[*returnSize] = 3;
//行数++
(*returnSize)++;
//左右指针去重
while(j < k && nums[j] == nums[++j]);
while(j < k && nums[k] == nums[--k]);
}
else if (sum < 0)
j++;
else
k--;
}
}
return ret;
}