❀❀❀ 文章由@不准备秃的大伟原创 ❀❀❀
♪♪♪ 若有转载,请联系博主哦~ ♪♪♪
❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤
三数之和
题目来源LeetCode:刷题传送门
题目:给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。
请你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
由题目可以得到我们呢需要求的是一个数组内的三个和为0的元素,且需要是不同的三个元素。
其实当我们进去做题目的时候会发现提干处会有如下一段英文
/*
* 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().
*/
简单翻译一下就是:
返回一个大小为 *returnSize 的数组,数组中的每个元素都是一个数组。
数组的大小作为 *returnColumnSizes 数组返回。
注意:返回的数组和 *columnSizes 数组都必须使用 malloc 进行分配内存,假设调用者会调用 free() 进行释放。
在C语言中我们不仅要实现代码,还需要考虑*returnSize和*returnColumnSize的开辟,这一点其实是在做题时C比其他语言要繁琐的地方
算法一:暴力求解
同样的我们先来最简单也最容易想到的暴力求解:使用三个for循环一个一个找:(伪代码实现)
//由于这段代码有很多的细节没有考虑,所以暂时就先不管returnSize和returnColumnSize的开辟了
int** ans = malloc(sizeof(int*)*numsSize)
for(int i = 0; i < numsSize - 2; i++){for(int j = i + 1; j < numsSize - 1; j++){for(int k = numsSize - 1; k > 1; k--){
ans[*returnSize][0] =nums[i];//ans为一个二维数组
ans[*returnSize][1] =nums[j];
ans[*returnSize][2] =nums[k];
(*returnSize++);
//如果满足ans[i] + ans[j] + ans[k] == 0 则存入ans里
}
}
}
return ans;
这一种方法很笨,但是也很容易想到,时间复杂度是O(N*N*N)。
那我们来想一想这一段代码除了没有考虑returnSize的returnColumnSize的开辟,其他有没有什么别的问题?
是的,聪明且细心的铁汁一定发现了,题目要求:不同的三元组,所以我们还需要写一段代码判断是否有重复的答案存在。
好的,到这里铁汁们是不是头都要秃了啊?○( ^皿^)っHiahiahia… 那这样,我们先来看一条类似的但更简单的题目:
两数之和
题目来源LeetCode:刷题传送门
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值target
的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
返回的答案必须使用 malloc 进行分配内存,假设调用者会调用 free() 进行释放。
思想一:暴力求解
太简单了,所以博主直接放代码了:
int* twoSum(int* nums, int numsSize, int target, int* returnSize)
{for(int i = 0; i < numsSize; i++){for(int j = i + 1; j < numsSize; j++){if(nums[i] + nums[j] == target){*returnSize = 2;int* ans = (int*)malloc(sizeof(int) * 2);ans[0] = i; ans[1] = j; return ans;}}} *returnSize = 0;return NULL;
}
思想二:排序+双指针
看过博主上一篇好题分享的铁汁们应该对双指针有所了解了,其实在熟练掌握了排序+双指针之后像这种类似的题目的第一个想法就应该是双指针了。
两数之和比三数之和简单的原因不仅是数字变少了,而且最终的答案也只有一组解,不需要考虑去重,需要开辟的空间内容也少了。
不知道铁汁们还记不记得上一次的双指针思路了,不记得的话....:--->盛水最多的容器
好的,我们大概回顾一下哈~:我们所谓的双指针呢,不是真的指针,而是用两个数来表示数组的下标,一般一个指向第一个元素,一个指向最后一个元素,在不同的情况下对两个数进行不同的操作。
嗯...怎么说呢,这道题想到双指针本身是很好的想法,但是我们答案需要我们返回的是元素下标,所以如果我们使用qsort排序后,下标就会被打乱,这样返回的答案就会有错误。 那如果我们改一下,最后的答案求的是两个值,那么双指针就是嘎嘎棒了!
虽然求两个数的和我们不可以用双指针,但是我们可以把思维套用到求三个数的和的那个题目上:
三数之和の思想二:排序+双指针
首先我们先把nums排个序,因为我们的方法是以排序为起点的嘛:
int cmp(const int* p1,const int* p2){return *p1 - *p2;}
//函数内
qsort(nums,numsSize,sizeof(int),cmp);
首先我们先实现代码的主体:不管怎么说,实现题目的需求才是第一重要嘛:
int j = 0;//这里引用一个变量j,表示的答案有多少个,即returnSize,所以只要最后=一下就可以了
int left = 0;int right = i - 1;while(left < right){if(nums[left] + nums[right] + nums[i] == 0) {ans[j] = (int*)malloc(sizeof(int) * 3);ans[j][0] = nums[left];ans[j][1] = nums[right];ans[j][2] = nums[i];j++;left++;right--;}else if(nums[left] + nums[right] + nums[i] > 0)right--;elseleft++;
好,上面我们已经实现了主体,接下来我们需要考虑*reuturnSize和*returnColumnSize 内存的开辟:但是~先别急,我们在写答案的时候如果没有可以返回的答案能行吗? 当然不行了啊,所以呢,在考虑其他细节之前,我们要把答案存到一个二维数组里面啊!
int** ans = malloc(sizeof(int*)*numsSize*numsSize);
//行ans[j][0] = nums[left];ans[j][1] = nums[right];ans[j][2] = nums[i];
//三列,在暴力求解里已经写过了
这时候会有铁汁问了:“博主啊,你这个**ans为什么开辟了个numsSize*numsSize*sizeof(int) 大小的空间啊?” ,问得好,其实我们可以想象一下:
三个数和为0,当其中一个数变化,其他一个或者两个数肯定会变化的,而我们考虑最坏的情况,每次三个数只动两个数就可以满足和为0,那这是不是就类似两个 变量的循环,复杂度为O(N*N),最后不也就是numsSize*numsSize*sizeof(int) ?
但我们毕竟不能准确的计算需要的空间,开辟的大一点以防止越界。
接下来我们考虑*reuturnSize和*returnColumnSize 内存的开辟:由函数主体可以得到j就是*returnSize,所以不知不觉中我们就把他的值给算出来了,而*returnColumnSize就比较麻烦了:
*returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));
//行(同样的,开辟大一点没什么不好)
(*returnColumnSizes)[j] = 3;
//列(可以直接放在while循环内,每一行就是3个元素)
*returnSize = j;
这时候我们的代码就基本写好了,然后会有铁汁迫不及待的点个提交,然后“啪”的一下显示个错误,然后又回来骂博主了:“你这教的啥,都错的!”
诶诶诶,别啥事都赖我啊o(TヘTo) , 还亏我在文章开头夸你呢!现在我又得骂你记性不好了! (┬_┬)↘
忘了吧?是不是忘了?忘了去重了吧?!
其实实现去重也是比较简单的:
但是又有一个问题了:如果我们的案例是{0,0,0,0}的话不就会越界吗?所以我们还需要加个判断条件,代码实现如下:
while(left < right && nums[left] == nums[left + 1]) left++;while(right > left && nums[right] == nums[right - 1]) right--;
//千万记住left < right要放在==前面,因为&&的短路性质,当前面条件不成立的时候,就不会判断后面的条件,所以也就不会报错
最后我们只需要return ans;就可以完成我们的代码了,展示一下自己的成果吧:
int cmp(const int* p1,const int* p2){return *p1 - *p2;}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){qsort(nums,numsSize,sizeof(int),cmp);int** ans = malloc(sizeof(int*)*numsSize*numsSize);*returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));int j = 0;
for(int i = numsSize - 1; i > 1; i--)
{if(i < numsSize -1 && nums[i] == nums[i + 1]) continue;int left = 0;int right = i - 1;while(left < right){if(nums[left] + nums[right] + nums[i] == 0) {(*returnColumnSizes)[j] = 3;ans[j] = (int*)malloc(sizeof(int) * 3);ans[j][0] = nums[left];ans[j][1] = nums[right];ans[j][2] = nums[i];j++;while(left < right && nums[left] == nums[left + 1]) left++;while(right > left && nums[right] == nums[right - 1]) right--;left++;right--;}else if(nums[left] + nums[right] + nums[i] > 0)right--;elseleft++;}
}
*returnSize = j;
return ans;}
然后提交,通过!
这一路走下来真的很艰辛啊,但是无论什么道路都不会是轻松的,而且这也是我们成长的必经之路啊!但其实我们回过头来再仔细看看,想一想,这其实也没什么,当我们会这么觉得的时候,你已经成长了!
由于再过几天全国各大高校都要举行四六级考试了,博主在这里祝各位学子(当然包括博自己了)四六级一举通过! 绩点刷满!期末永不挂科!
那么本篇博客也就到此为止了,给大家送上个祝福,勉励自己以及这世界上所有追逐梦想的赤子趁年华尚好努力提升自己,莫欺少年穷!(看博主这么诚心诚意,给个赞不过分吧!ヾ(^▽^*))) )