leetcode 15.三数之和(C语言)
题目链接:leetcode 15.三数之和
题目描述:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
题解: 第一眼看这题一般首先会觉得很简单,不就是直接枚举嘛,后来看到不包含重复觉得问题并不是很简单,有仔细想想超时的问题,方法又 变得有待优化,再仔细一看题目还没有限制数据长度范围。仔细推敲后发现这还是一道不是那么简单的题目。但是等我们用起C语言开始写代码时,更是会遇到种种问题,因为C语言并不想其他语言那么方便简洁,所以也很考验我们C语言学的怎么样。
思路: 在讲解思路之前,我们可以先回忆另外一道题leetcode 1.两数之和,看完这道题,再联想这道题,估计就有了思路。题目中让我们找到a+b+c=0,我们可以把a当做求解两数之和的target。 然后我认为还是在穷举的前提下做优化,优化方法还是两种,即双指针和二分优化。
还是先上代码;
int cmp(const void*a,const void*b) //qsort的比较函数
{
return *(int*)a-*(int*)b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize=0;
if(numsSize<3) //*returnSize=0;一定要注意放在这之前
return NULL;
qsort(nums,numsSize,sizeof(nums[0]),cmp); //stdio.h中的快速排序函数
int i=0,left=0,right=0,target=0,sum=0;
int** ans = (int**)malloc(sizeof(int*)*(numsSize)*(numsSize )); //为返回结果的数组分配空间,至于为什么是numsSize*numsSize,后面会说明
*returnColumnSizes = (int*)malloc(sizeof(int)*(numsSize)*(numsSize)); //这个是存放每一行的列数的数组,实际上不用算,就是3
for(i=0;i<numsSize-2;i++){
if(nums[i]>0)
break; //因为是从大到小序列,a,b,c>0,等式恒不成立
while(i>0&&i<numsSize-2&&nums[i]==nums[i-1])
i++; //选取target去重
target=-nums[i];
left=i+1;
right=numsSize-1;
while(left<right){ //双指针
sum=nums[left]+nums[right];
if(sum<target){
left++; //while(left<right&&nums[left]==nums[++left]);可以在这里也去重,但没必要
continue;
}
if(sum>target){
right--;
continue;
}
ans[*returnSize]=(int*)malloc(sizeof(int)*3);
ans[*returnSize][0]=-target;
ans[*returnSize][1]=nums[left];
ans[*returnSize][2]=nums[right];
(*returnColumnSizes)[*returnSize]=3;
*returnSize=*returnSize+1;
while(left<right&&nums[left]==nums[++left]);
while(right>left&&nums[right]==nums[--right]);//去重
}
}
return ans;
}
注释中就有一些小细节,还有就是关于数组大小问题。
我认为是可以推算出(numsSize*numsSize)的空间是完全足够的。
要使得a+b+c=0;(假定a<b<c)
那么就必定有:a<0&&c>0恒成立
然后假设将nums排序后有x个负数,n-x个正数
a有x种可能的值,c有(n-x)种可能的取值,假设无论a和c在任意允许的取值情况都有对应的b存在(这种假定虽然不存在),那么空间就是n*(n-x),所以我认为分配(numsSize*numsSize)的数组大小是完全够的。
可能还可以把范围再降低一个级别,如果你知道,请一定私信或者在评论区告知你的方法。
**可能还有人会尝试用C语言里面的realloc函数,**我也尝试了很久,但是都失败了,realloc本身是一个容易造成问题的函数。
我用的realloc分配数组空间提交后,显示超出时间限制,有两个测试用例没过就超时了,如果你们有用realloc函数通过测试的,希望你能私信,或者在评论区留言。
/**
* 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().
*/
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)
return NULL;
qsort(nums,numsSize,sizeof(nums[0]),cmp);
int i=0,left=0,right=0,target=0,sum=0;
int **ans=(int**)malloc(sizeof(int*));
*returnColumnSizes = (int*)malloc(sizeof(int) * (numsSize - 2) * (numsSize - 2));
for(i=0;i<numsSize-2;i++){
if(nums[i]>0)
break;
while(i>0&&i<numsSize-2&&nums[i]==nums[i-1])
i++;
target=-nums[i];
left=i+1;
right=numsSize-1;
while(left<right){
sum=nums[left]+nums[right];
if(sum<target){
while(left<right&&nums[left]==nums[left+1])
left++;
left++;
continue;
}
if(sum>target){
while(right>left&&nums[right]==nums[right-1])
right--;
right--;
continue;
}
ans=(int**)realloc(ans,sizeof(int*)*(*returnSize+1));
ans[*returnSize]=(int*)malloc(sizeof(int)*3);
ans[*returnSize][0]=-target;
ans[*returnSize][1]=nums[left];
ans[*returnSize][2]=nums[right];
(*returnColumnSizes)[*returnSize]=3;
*returnSize=*returnSize+1;
while(left<right&&nums[left]==nums[++left]);
while(right>left&&nums[right]==nums[--right]);
}
}
return ans;
}