leetcode:三数之和详解。

题目描述

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

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

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

首先讨论其简化版本两数之和的的情况

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素

示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

  1. for循环两遍遍历(c++代码)。
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> b(2,-1);//用来承载结果,初始化一个大小为2,值为-1的容器b
        for(int i=0;i<nums.size()-1;i++)
        {
            for(int j= i+1 ;j<nums.size();j++){
                if(nums[i]+nums[j]==target){
                    b[0] = i;
                    b[1] = j;
                    break;
                }
            }
        }
        return b;
    };
};
  1. 建立hash表,减小遍历层数(js代码,利用js数组的动态性建立hash,也可使用map)。
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    var res = new Array();
    var hash = new Array();
    for(var i = 0;i<nums.length;i++){
        hash[nums[i]] = i;//建立hash表,将数组的值作为序号
    }
    for(i = 0;i<nums.length-1;i++){
        if(hash[target-nums[i]]!=undefined&&i!=hash[target-nums[i]]){//在表中查找是否存在是否存在
            res[0] = i;
            res[1] = hash[target-nums[i]];
        }
    }
    return res;
};

只遍历一遍,时间复杂度减小一个数量级。

因为不与自身比较,可以继续优化,在遍历时建hash表。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    var res = new Array();
    var hash = new Array();//优化,在遍历时建立hash表
    for(i = 0;i<nums.length;i++){
        if(hash[target-nums[i]]!=undefined){//在表中查找是否存在是否存在
            res[0] = i;
            res[1] = hash[target-nums[i]];
        }
        hash[nums[i]] = i;
    }
    return res;
};

三数之和解答

  1. 首先最笨的方法,采用for循环3层遍历。
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> temp(3);
        for(int i = 0;i<nums.size()-2;i++){
            for(int j = i+1;j<nums.size()-1;j++){
                for(int k = j+1;k<nums.size();k++){
                    if(nums[i]+nums[j]+nums[k]==0){
                        temp[0] = nums[i];
                        temp[1] = nums[j];
                        temp[2] = nums[k];
                        res.push_back(temp);
                    }
                }
            }
        }
        return res;
    }
};

这种办法时间复杂度最高,且由与原给定数组中数存在相同的元素,导致返回的数组中会出现大量重复的情况。
2. 同二数之和,可以建立hash表减少循环嵌套,同样存在。

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    var res = new Array();
    var hash = new Array();
    var j;
    for( j = 0;j<nums.length;j++){
        hash[nums[j]] = j;//建立hash
    }
    for(var i = 0;i<nums.length-2;i++){
        for(j = i+1;j<nums.length-1;j++){
            if(hash[-nums[i]-nums[j]]!=undefined&&hash[-nums[i]-nums[j]]>j){//检查hash是否存在,并确保与前两个数不同
                var temp = new Array();//引用
                temp[0] = nums[i];
                temp[1] = nums[j];
                temp[2] = -(nums[i]+nums[j]);
                res.push(temp);
            }
            
        }
    }
        return res;
};
  1. 对于这种求固定和的问题,我们还可以先对数组进行排序,然后从两边进行查找,这样还可以解决重复的问题。
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> temp(3);
        sort(nums.begin(),nums.end());//首先对nums进行排序
        int i ,j, k;
        if(nums.size()<3||nums[0]>0||nums[nums.size()-1]<0) return res;
        for(i = 0 ;i<nums.size()-2;){
            if(nums[i]>0) break;//最左值大于零排除
            for(j = i+1,k = nums.size()-1;j<k;){
               if(nums[i]+nums[j]+nums[k]==0){                  
                    temp[0] = nums[i];
                    temp[1] = nums[j];
                    temp[2] = nums[k];
                    res.push_back(temp);
                   do{                    
                       j++;
                       if(j>=k) break;
                   }while(nums[j]==nums[j-1]);
                   do{
                       k--;
                       if(j>=k) break;
                       if(nums[k]>0) break;
                   }while(nums[k]==nums[k+1]);
                }else if(nums[i]+nums[j]+nums[k]>0){
                   do{
                       k--;
                       if(j>=k) break;
                       if(nums[k]>0) break;
                   }while(nums[k]==nums[k+1]);                
                }else{
                   do{
                       j++;
                       if(j>=k) break;
                   }while(nums[j]==nums[j-1]);
                }  
            }
            do{
                i++;
                if(i>=nums.size()-2)  break;
            }while(nums[i]==nums[i-1]);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值