zoukankan      html  css  js  c++  java
  • Leetcode-15. 三数之和

    题目

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

    满足要求的三元组集合为:
    [
    [-1, 0, 1],
    [-1, -1, 2]
    ]

    思路

    要求的是a+b+c=0 其实就是要求a+b=-c,那么问题可以转化为依次遍历数组元素c,然后在剩下的数中做两数之和为-c的问题。
    问题在于如何简化算法以及优化复杂度。 1.首先可以先排序(O(nlogn)),这样保证数组有序之后可以利用大小关系判断。
    2.设置两个指针left、right,分别从左边以及右边向中间遍历,如果找到a+b+c==0,那么可以将这个答案加入到答案集里 如果a+b+c<0,此时固定的是c,说明a+b太小了,因此left+=1;如果a+b+c>0,此时a+b过大,因此right-=1
    3.去重,这一步则是利用了有序性,如果两个数相同,那他们在数组的位置一定是相邻的(连着几个数相同也是可能的),因此 去重的操作就能简单遍历一下相邻的是否相同即可。由于数组有序性使得去重这一步很简单,因此也可以看出第一步的作用。
    此外还有一些小细节的地方,比如说当遍历到c>0的时候,由于之后的数都是正数,那三数之和一定大于0,就没必要继续遍历c了(因为 继续向后遍历c只会更大,那之后的数加起来一定大于0); 或者固定c,如果c及其后面连着两个数a,b,他们的和已经大于0了,就没必要进行下一步的操作,此时遍历下一个c; 同理,如果c和数组最后两个数的和仍然小于0,也没必要进行下一步操作。
     

    代码

    //最大答案数
    #define MAX_ANS_NUM 1000000
    
    int comp(const void* a, const void* b) {
        return *((int*)a) - *((int*)b);
    }
    
    int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
        int** result = (int**)calloc(MAX_ANS_NUM, sizeof(int*));
        *returnColumnSizes = (int*)calloc(MAX_ANS_NUM, sizeof(int));
        *returnSize = 0;
        if (numsSize < 3) return result;
        //先将入参排序
        qsort(nums, numsSize, sizeof(int), comp);
        //排除极端异常情况:
        if (nums[0] + nums[1] + nums[2] > 0) return result; // 最小三个数相加都大于0直接无解
        if (nums[numsSize - 1] + nums[numsSize - 2] + nums[numsSize - 3] < 0) return result; //最大三个数相加都小于0直接无解
    
        int left, right, anscount;
        anscount = 0;
        for (int i = 0; i < numsSize - 2; i++) 
        {
            if (nums[i] + nums[i + 1] + nums[i + 2])
            {
                if (nums[i] + nums[numsSize - 1] + nums[numsSize -2] < 0) 
                continue; // 剪枝优化无效判断 最小的加上 最大的两个数都小于0继续找下一轮
            }
          
            if (i > 0 && nums[i] == nums[i - 1]) 
            {
                continue; // 重复的判断
            }
    
            left = i + 1;
            right = numsSize - 1;
    
            while (left < right) 
            {
                if (nums[left] + nums[i] + nums[right] == 0) 
                {
                    result[anscount] = (int*)calloc(3, sizeof(int));
                    result[anscount][0] = nums[left];
                    result[anscount][1] = nums[i];
                    result[anscount][2] = nums[right];
                    (*returnColumnSizes)[anscount] = 3;
                    anscount++;
                    while(left + 1 < right && nums[left] == nums[left + 1]) 
                    {
                        left++;
                    }
                    left++;
                    while (left < right - 1 && nums[right] == nums[right - 1]) 
                    {
                        right--;
                    }
                    right--;
                } else if (nums[left] + nums[i] + nums[right] < 0) 
                {
                    left++;
                } else if (nums[left] + nums[i] + nums[right] > 0) 
                {
                    right--;
                }
            }
        }
    
        *returnSize = anscount;
        return result;
    }
     
  • 相关阅读:
    live555源码研究(三)------UsageEnvironment类
    live555源码研究(二)------TaskScheduler类
    live555源码研究(一)------live555MediaServer的启动过程和基本类图
    (转)视频监控相关文章
    【流媒體】live555—VS2008 下live555编译、使用及测试
    【转】PostgreSQL IP地址访问配置
    red5研究(一):下载,工程建立、oflaDemo安装、demo测试
    SVN服务器的搭建和使用
    【转】linux下cvs配置
    【转】js正则表达式语法
  • 原文地址:https://www.cnblogs.com/mhq-martin/p/12019500.html
Copyright © 2011-2022 走看看