15. 三数之和

277 阅读1分钟

题目描述

给你一个包含 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-cn.com/problems/3s…

分析

要求数组中a+b+c=0的三个数a,b,c,暴力破解的话需要三层for循环,时间复杂度过高无法通过 因此,我们可以做些优化,最外层循环不变,里面的两层循环用双指针优化为一层。 具体实施:

  1. 先对数组进行排序
  2. 对数组进行循环获取a
  3. 双指针left,right分别指向a+1和numsSize-1
  4. 当a+b+c>0时,说明b+c略大了,由于数组已排序,因此right指针往左移
  5. 当a+b+c<0时,说明b+c略小了,因此left指针往右移
  6. 去重,当left往右移的值和left相同时(right指针左移后的值与right相同时),left继续右移(right继续左移)。这时由于最外层for循环确定了a,left确定了b,a+b+c=0的话,c也就是确定的,如果left右移后的值与left时相同,那么c的值也是相同的,a,b,c就全相同了,与前一个是重复的三元组

举例:nums=[-1,0,1,2,-1,-4]

  1. 先排序nums=[-4,-1,-1,0,1,2]
  2. a=-4时,left指向-1,right指向2
  3. -4+-1+2=-3<0,left++,此时left指向-1与前一个left相同,直接继续left++,指向0
  4. 此时-4+0+2=-2<0,left++,此时left指向1
  5. 此时-4+1+2=-1<0,left++,此时left与right相遇,开始第二轮循环
  6. 第二轮循环a=-1,left指向-1,right指向2
  7. -1+-1+2=0,满足条件保存三元组[-1,-1,2],left++ ...后面类似

实现

int compare(const void *a, const void *b)
{
    return *(int*)a - *(int*)b;
}

int **threeSum(int *nums, int numsSize, int* returnSize, int **returnColumnSizes)
{
    *returnSize = 0;    // returnSize返回三元组的个数
    if (numsSize < 3) {
        return NULL;
    }
    
    qsort(nums, numsSize, sizeof(nums[0]), compare);
    if (nums[0] > 0 || nums[numsSize-1] < 0) {
        return NULL;
    }
    *returnColumnSizes = (int*)malloc(sizeof(int*) * numsSize * numsSize);
    int **res = (int**)malloc(sizeof(int*) * numsSize * numsSize);  // 可以存放的三元组的个数就是数组个数的全排列n!/(n-3)!=n*(n-1)*(n-2),但是malloc溢出,这里取n*n
    for (int i = 0; i < numsSize; i++) {
        if (i > 0 && nums[i] == nums[i-1]) {
            continue;
        }
        int left = i + 1;
        int right = numsSize - 1;
        while (left < right) {
            if (nums[i] + nums[left] + nums[right] > 0) {
                right--;
            } else if (nums[i] + nums[left] + nums[right] < 0) {
                left++;
            } else {
                res[*returnSize] = (int*)malloc(sizeof(int) * 3);
                (*returnColumnSizes)[*returnSize] = 3;
                res[*returnSize][0] = nums[i];
                res[*returnSize][1] = nums[left];
                res[*returnSize][2] = nums[right];
                (*returnSize)++;
                left++;
                right--;

                // 去重
                while (right > 0 && nums[right] == nums[right+1]) {
                    right--;
                }
                while (left < numsSize - 1 && nums[left] == nums[left-1]) {
                    left++;
                }
            }
        }
    }
    return res;
}