《LeetCode之每日一题》:10. 三数之和(C)

三数之和


题目链接: 三数之和

有关题目

给你一个包含 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
-10^5 <= nums[i] <= 10^5

题解

参考官方题解
参考小镇做题家题解

我们可以在O(N^3)三重循环下把结果算出来,但是时间复杂度太大了
不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:

第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;

第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。

也就是说,我们枚举的三元组 (a, b, c) ,a≤b≤c,保证了只有 (a, b, c)(a,b,c) 这个顺序会被枚举到,而 (b, a, c)(b,a,c)(c, b, a)(c,b,a) 等等这些不会,这样就减少了重复。

同时我们还要保证相邻的两个元素,不能重复
第一重循环,时间复杂度为 O(N)*O(N),因此枚举的总时间复杂度为 O(N^2)。
由于排序的时间复杂度为 O(NlogN),在渐进意义下小于前者
因此算法的总时间复杂度为 O(N^2)
2

解题思路
1、判断输入数组为空、或元素个数小于3个,直接返回NULL

2、将输入数组升序排序

33.1、判断当前元素是否为正,是则结束循环,因为后续的数字也都为正,不会出现三数之和为0的情况
    3.2、判断当前元素是否与上一次相等,相等则跳过进行去重
    3.3、从 i + 1 到 numsSize - 1 进行左右指针判断三数之和sum:
        3.3.1、sum == 01、找到一组值,进行保存
            2、对left、right进行去重
        3.3.2、sum < 0 :
            left++
        3.3.3、sum > 0
            right--
其中参数 int** returnColumnSizes 表示返回数组中每一行的列数:
分配:
    *returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));
使用:
    (*returnColumnSizes)[*returnSize] = 3;    /* 返回数组当前行的列数为3 */

其中返回数组ret为类似二维数组:
分配:
    int** ret = (int**)malloc(numsSize * numsSize * sizeof(int*));
    ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
使用:
    ret[*returnSize][0] = nums[i];

/*
 * 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().
 */
 //qsort的比较函数
int cmp_int (const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int**returnColumnSizes){
	/* 先记录返回的行数为0 */
    *returnSize = 0;
	/* 输入为空、或元素个数小于3则返回NULL */
	if(nums == NULL || numsSize < 3)
		return NULL;
	int first = 0,second = 0;
	/* 将nums排序为升序排列 */
	qsort(nums,numsSize,sizeof(int),cmp_int);
	/* 分配返回数组、返回数组的列数 */
	int** ret = (int**)malloc( sizeof(int*) * numsSize* numsSize);//<--这个地方中的开辟空间?//sizeof(int*) * numsSize * numsSize????
	
	*returnColumnSizes = (int*)malloc(numsSize* numsSize * sizeof(int));//返回数组的行数
	for (first = 0; first < numsSize; first++)
	{
		//第一个数字大于0直接break
		if (nums[first] > 0)
			break;
		//保证和上一次枚举的数字不一样
		if (first > 0 && nums[first - 1] == nums[first] )
			//这边就因为 first 写到了后面,就在力扣出现了非法访问内存区域的
			//提示,浪费了大量时间,要细心啊!!!
				continue;
		int third = numsSize - 1;//指向数组的最右端
		int second = first + 1;
		while(second < third)
		{
			int sum = nums[first] + nums[second] + nums[third];
			if ( sum == 0 )
			{
				ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
				ret[*returnSize][0] = nums[first];
				ret[*returnSize][1] = nums[second];
				ret[*returnSize][2] = nums[third];
				/* 返回数组当前行的列数为3 */
                (*returnColumnSizes)[*returnSize] = 3;
				//行数加1
				(*returnSize)++;
				//对指向b,c的指针进行去重操作
				while(second < third && nums[second] == nums[++second]);
				while(second < third && nums[third] == nums[--third]);
			}
			else if (sum < 0)
				second++;
			else
				third--;
		}	
	}
        return ret;
}

这两种解法大致都一个意思

/**
 * 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().
 */
//当然我们这里可能开辟的数组空间大小太大了,所以我么可以根据numsSize的大小
//来开辟空间
 #define nums_max_length 3000
 //快排的比较函数
 int cmp_by_int (const void* e1, const void* e2)
 {
     return *(int*)e1 - *(int*)e2;
 }
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    *returnSize = 0;//返回个数先置零

    //3个以下元素或者无元素排除
    if (numsSize < 3 || nums == NULL)
        return NULL;

    //按照上面题干给的要求动态开辟返回数组和返回数组每一行的列数
    int** ret = (int**)malloc(sizeof(int*) * nums_max_length *nums_max_length);
    *returnColumnSizes = (int*)malloc(sizeof(int) * nums_max_length * nums_max_length);

    //升序排序
    qsort(nums,numsSize,sizeof(int),cmp_by_int);
    int i = 0;
    for (i = 0; i < numsSize; i++)
    {
        //第一个元素为真又由于升序,直接排除
        if (nums[i] > 0)
            break;

        //由于不能重复,保证相邻两个元素不相等
        //去重
        if (i > 0 && nums[i] == nums[i - 1])
            continue;

        int j = i + 1;//左指针
        int k = numsSize - 1;//右指针
        while(j < k)
        {
            int sum = nums[i] + nums[j] + nums[k];//三数之和
            //判断sum与0的关系
            if(sum == 0)
            {
                //记录三数
                ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
                ret[*returnSize][0] = nums[i];
                ret[*returnSize][1] = nums[j];
                ret[*returnSize][2] = nums[k];

                //记录当前数组的列数
                (*returnColumnSizes)[*returnSize] = 3;

                //行数++
                (*returnSize)++;

                //左右指针去重
                while(j < k && nums[j] == nums[++j]);
                while(j < k && nums[k] == nums[--k]);
            }
            else if (sum < 0)
             j++;
            else
             k--;
        }
    }
    return ret;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值