目录
1.题目要求
(1) 题目理解:
(2)思路分析:
3.代码分部讲解
题目要求
给你一个包含 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
-105 <= nums[i] <= 105来源:力扣(LeetCode)
这题乍一看是不是很熟悉?没错,我的之前写了一篇 “两数之和” 的博客,跟这个题很相似,但是做法却更难一点。看这篇博客前建议大家可以参考之前的那一篇博客加深一下理解。
这一次我们不用 “哈希表” ,而是介绍一下我们并不陌生的 “双指针” ,对于双指针,我上一篇文章也有讲解过,大家可以根据自身情况去了解。
https://blog.csdn.net/m0_63988748/article/details/125255208(最多盛水问题——双指针)
题目理解以及思路分析
题目理解:
就是从给出的数组中找到符合要求的三个数字,然后组成一个二维数组
思路分析:
(一):
三个数字,我们先定义一个 “双指针” ,但是这两个指针的位置不是随便的。
我们的指针分别为 左指针—— L ,右指针—— R 。由图可以看出,指针初始的位置就是这样。那我们如何进行遍历呢?要知道一个好的遍历方法会让问题事半功倍。
起始位置的 sum = i + L + R = 1 ,可见 sum > 0 。因此我们让 右指针左移,然后接着判断这一次的 sum 的值,如果 sum < 0 ,则 左指针右移,直至左右指针重合。然后 i + 1,开始下一轮的遍历。
这样是不是对这个题目有了更清晰的理解和认识?
(二):
明白了上述的思路,接下来我们就要进行的是如何让遍历更加的简便。
1.给数组排序,使其按照从小到大的顺序排列。
2.对于 nums = NULL 以及numsSize < 3 的全部返回 NULL。
3.对于排过序的数组,凡是 nums [ i ] = 0 的,全部返回 0.
(三):
下面就只剩最后一个问题了——去重。这一个我们结合具体的代码进行讲解。
代码分部讲解
第一部分:
//排序,使其代码按照从低到高的顺序排列
int inc(const void*a,const void*b)
{
return* (int*)a - * (int*)b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize = 0;
int i;
if(nums == NULL||numsSize < 3) //根据题意可以先排除一部分
return NULL;
qsort(nums,numsSize,sizeof(int),inc); //升序排列
着重讲解:
int inc ( const void* a, const void* b)
{
return* ( int* ) a - * ( int* ) b;
}这个是C语言中的排序函数,其中 inc 全拼是 increase(增加),代表着 “升序排列” ,这个名字也可以随意取。
qsort ( nums, numsSize, sizeof(int), inc);
qsort函数,与上面的是一体的。
其函数通常由四个参数构成
- 第一个参数为待排序数组首地址。
- 第二个参数为数组长度(常用sizeof [ a [ n ] ])来代表
- 第三个参数为数组元素所占字节
- 第四个参数为所调用函数的指针,函数名即是函数的指针,可直接写函数名,调用函数用来确定排序的方式
第二部分:
//分配返回数组的列数
*returnColumnSizes = (int*)malloc(sizeof(int)*numsSize* numsSize);
//分配返回数组
int** han = (int**)malloc(sizeof(int*)*numsSize* numsSize);
for(i=0;i<numsSize;i++)
{
if(nums[i]>0) //对于排过序的数组来说,如果nums[i] = 0,则三数之和不可能为 0
break;
if(i>0 && nums[i]==nums[i-1]) //如果与上一个数相同则跳过(去重)
continue;
//分配返回数组的列数
*returnColumnSizes = (int*) malloc ( sizeof(int* )* numsSize* numsSize);
//分配返回数组
int** han = (int**) malloc (sizeof( int* )* numsSize* numsSize);这个是“动态分配”,malloc函数。为其分配一个动态的空间。
第三部分:
int L = i+1, R = numsSize-1; //定义左右指针
while(L < R) //循环
{
int sum = nums[i] + nums[L] + nums[R]; //三数之和
int n,m;
if(sum == 0) //如果 三数之和为零
{
han[*returnSize] = (int*)malloc(sizeof(int)*3);//为二维数组的行数分配三个空间
han[*returnSize][0] = nums[i];
han[*returnSize][1] = nums[L];
han[*returnSize][2] = nums[R];
(*returnColumnSizes)[*returnSize] = 3; //返回数组当前行的列数为3
*returnSize+=1; //行数加 1
//左右去重
do{
n=L++;
}while(n<R && nums[L] == nums[n]);
do{
m=R--;
}while(L<m && nums[R] == nums[m]);
}
else if(sum < 0)
L++;
else if(sum > 0)
R--;
}
}
return han; //返回数组
}
着重讲解:
han[*returnSize] = (int*)malloc(sizeof(int)*3);//为二维数组的行数分配三个空间
han[*returnSize][0] = nums[i];
han[*returnSize][1] = nums[L];
han[*returnSize][2] = nums[R];
(*returnColumnSizes)[*returnSize] = 3; //返回数组当前行的列数为3
*returnSize+=1; //行数加 1这一部分跟之前的“两数之和”里面很相似,所以就不过多的讲解
这里需要注意的一点是:
*returnSize+=1
不可以写成
*returnSize++
因为,优先级的问题会导致代码出现错误。
我们可以将其改为
(*returnSize)++
do{ n=L++; }while(n<R && nums[L] == nums[n]); do{ m=R--; }while(L<m && nums[R] == nums[m]);
这里的目的就是,当sum == 0 的时候,对左右指针下一次的移动进行去重。
当然,很多人会选择写另一种代码程序:
while(L < R && nums[L] == nums[++L]); while(L < R && nums[R] == nums[--R]);
这种写法是非常不规范的,但是用起来确实极度的舒适;
这里讲解一下这串代码
while(xxxxxxx);
后面加了一个 “ ; ”,就意味着,当里面的条件为 “真” 时,部执行其下面的代码,当为 “非” 时,便会去执行。
因此,上述的代码意思便可以解释为,令原本的 nums[L] 和 nums[R] 的值等于其右/左一个的值,以此类推直至不符合条件结束,这就达到了左右去重的目的。
附上完整的代码:
/**
* 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 inc(const void*a,const void*b)
{
return* (int*)a - * (int*)b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize = 0;
int i;
if(nums == NULL||numsSize < 3)
return NULL;
qsort(nums,numsSize,sizeof(int),inc);
*returnColumnSizes = (int*)malloc(sizeof(int)*numsSize* numsSize);
int** han = (int**)malloc(sizeof(int*)*numsSize* numsSize);
for(i=0;i<numsSize;i++)
{
if(nums[i]>0)
break;
if(i>0 && nums[i]==nums[i-1])
continue;
int L = i+1, R = numsSize-1;
while(L < R)
{
int sum = nums[i] + nums[L] + nums[R];
int n,m;
if(sum == 0)
{
han[*returnSize] = (int*)malloc(sizeof(int)*3);
han[*returnSize][0] = nums[i];
han[*returnSize][1] = nums[L];
han[*returnSize][2] = nums[R];
(*returnColumnSizes)[*returnSize] = 3;
*returnSize+=1;
//(*returnSize)++;
/*非常不规范的写法,但是极其舒适!
while(L < R && nums[L] == nums[++L]);
while(L < R && nums[R] == nums[--R]);
*/
do{
n=L++;
}while(n<R && nums[L] == nums[n]);
do{
m=R--;
}while(L<m && nums[R] == nums[m]);
}
else if(sum < 0)
L++;
else if(sum > 0)
R--;
}
}
return han;
}