目录
1.题目要求
2.题目理解以及思路分析
(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)
给你一个包含 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)
这题乍一看是不是很熟悉?没错,我的之前写了一篇 “两数之和” 的博客,跟这个题很相似,但是做法却更难一点。看这篇博客前建议大家可以参考之前的那一篇博客加深一下理解。
两数之和(LeetCode)_简十三的博客-CSDN博客问题描述给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。示例一:示例二:示例三: 提示: 2 ......https://blog.csdn.net/m0_63988748/article/details/125135465
这一次我们不用 “哈希表” ,而是介绍一下我们并不陌生的 “双指针” ,对于双指针,我上一篇文章也有讲解过,大家可以根据自身情况去了解。
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;
}
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;i0) //对于排过序的数组来说,如果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函数。为其分配一个动态的空间。
//分配返回数组的列数
*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 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这里的目的就是,当sum == 0 的时候,对左右指针下一次的移动进行去重。
当然,很多人会选择写另一种代码程序:
while(L < R && nums[L] == nums[++L]); while(L < R && nums[R] == nums[--R]);这种写法是非常不规范的,但是用起来确实极度的舒适;
这里讲解一下这串代码
while(xxxxxxx);
后面加了一个 “ ; ”,就意味着,当里面的条件为 “真” 时,部执行其下面的代码,当为 “非” 时,便会去执行。
因此,上述的代码意思便可以解释为,令原本的 nums[L] 和 nums[R] 的值等于其右/左一个的值,以此类推直至不符合条件结束,这就达到了左右去重的目的。



