LeetCode-两数之和&三数之和

两数之和(easy)

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

 

方法一:暴力求解

很自然的方法就是依次遍历数组中每个数,在剩下的数中找是否存在和为target的数,返回他们的下标。代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ret;
        ret.clear();

        if(nums.size() == 0){
            return ret;
        }

        for(int i=0; i<nums.size()-1; ++i){
            for(int j=i+1; j<nums.size(); ++j){
                if(nums[i] + nums[j] == target){
                    ret.push_back(i);
                    ret.push_back(j);
                    return ret;
                }
            }
        }

        return ret;
    }
};

时间复杂度O(n^2)

 

方法二:hash表

利用一个哈希表,通过O(n)的空间消耗,将时间复杂度降低到O(n)。

哈希表的key存放数组的值,value是对应的下标。

遍历数组,在哈希表中查找是否存在和为target的数,不存在就将当前数加入哈希表,存在就返回value值(即数组中的下标)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ret;
        ret.clear();

        unordered_map<int, int> hash;
        for(int i=0; i <nums.size(); ++i){
            int findValue = target-nums[i];

            if(hash.find(findValue) != hash.end()){
                ret.push_back(i);
                ret.push_back(hash[findValue]);
                return ret;
            }else{
                hash[nums[i]] = i;
            }
        }

        return ret;
    }
};

关键是对unordered_map这个类型的熟悉使用。

时间复杂度降低了很大

 

 

三数之和(medium)

在上面的问题基础上引入三数之和

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

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

示例:

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

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

很自然的也想到用遍历的方法去寻找,自然时间复杂度是O(n^3),但是要求不能包含重复的三元组,这个在判断上就增加了复杂程度。

看到“避免重复”,可以想到将数组先进行排序,这样在遍历的时候,可以通过比较前后两个值的大小,跳过对重复数字的判断。

 

方法一:排序后暴力法

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());

        vector<vector<int>> result;
        result.clear();

        for(int i = 0; i<n-1; ++i){
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }

            for(int j=i+1; j<n-1; ++j){
                if(j>i+1 && nums[j]==nums[j-1]){
                    continue;
                }

                for(int k=j+1; k<n; ++k){
                    if(k>j+1 && nums[k]==nums[k-1]){
                        continue;
                    }

                    if(nums[i]+nums[j]+nums[k] ==0){
                        result.push_back({nums[i], nums[j], nums[k]});
                    }
                }
            }
        }
        return result;
    }
};

通过nums[j]==nums[j-1]来跳过对重复数字的遍历,但时间复杂度O(n^3),一般不满足要求。

 

方法二:排序后双指针法

排序后数字是以从小到大的顺序排序,那么在n[i]确定的情况下,如果n[j]+n[k]> -n[i],那么n[j]+n[k+1]肯定是大于-n[i]的,k只能朝数组左边移动。所以在从左到右遍历第二个数的时候,同时从右到左遍历第三个数,将两重遍历合并为一重遍历。同时要注意边界条件是j=k。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());

        vector<vector<int>> result;
        result.clear();

        for(int i = 0; i<n-1; ++i){
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }

            int k = n-1;
            int target = -nums[i];
            for(int j=i+1; j<n-1; ++j){
                if(j>i+1 && nums[j]==nums[j-1]){
                    continue;
                }

                while(j<k && nums[j]+nums[k]> target){
                    --k;
                }
                if(j == k){
                    break;
                }
                if(nums[j]+nums[k] == target){
                    result.push_back({nums[i], nums[j], nums[k]});
                }
            }
        }
        return result;
    }
};

时间复杂度:排序O(nlogn)+ 遍历O(n^2),一共O(n^2)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章