(LeetCode)三数之和——C语言

目录

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)

这题乍一看是不是很熟悉?没错,我的之前写了一篇 “两数之和” 的博客,跟这个题很相似,但是做法却更难一点。看这篇博客前建议大家可以参考之前的那一篇博客加深一下理解。

两数之和(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;
  }

这个是C语言中的排序函数,其中 inc 全拼是  increase(增加),代表着 “升序排列” ,这个名字也可以随意取。

qsort ( nums, numsSize, sizeof(int), inc);

qsort函数,与上面的是一体的。

其函数通常由四个参数构成

  1. 第一个参数为待排序数组首地址。
  2. 第二个参数为数组长度(常用sizeof [ a [ n ] ])来代表
  3. 第三个参数为数组元素所占字节
  4. 第四个参数为所调用函数的指针,函数名即是函数的指针,可直接写函数名,调用函数用来确定排序的方式

第二部分:

    //分配返回数组的列数
    *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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简十三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值