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)。

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