LeetCode (1) | 数组(c++)

Two Sum

未排序的数组,输出其中a+b=target的a和b的下标(只需输出一对即可)

思路:需要输出下标,故不能直接排序然后2sum,否则下标会有问题

使用空间换取时间,即使用Hashmap来建立数字和其坐标位置之间的映射,我们都知道HashMap是常数级的查找效率,这样,我们在遍历数组的时候,用target减去遍历到的数字,就是另一个需要的数字了,直接在HashMap中查找其是否存在即可,注意要判断查找到的数字不是第一个数字,比如target是4,遍历到了一个2,那么另外一个2不能是之前那个2

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)

(1)两遍哈希表

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        vector res;
        if(nums.size() < 2) return res;

        // 空间换时间
        map m;
        for(int i = 0;i < nums.size();i++){
            m[nums[i]] = i;
        }
        // 确定一个数,找另外一个数是否在字典中
        for(int i = 0;i < nums.size();i++){
            int t = target-nums[i];
            // 注意m[t] != i,不能是自身!
            if(m.count(t) && m[t] != i){
                res.push_back(i);
                res.push_back(m[t]);
                break;
            }
        }
        return res;
    }
};

(2)一遍哈希表

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        vector res;
        if(nums.size() < 2) return res;

        // 直接边插入边查找,效率更高
        map m;
        for(int i = 0;i < nums.size();i++){
            int t = target-nums[i];
            if(m.count(t) && m[t] != i){
                res.push_back(i);
                res.push_back(m[t]);
                break;
            }
            m[nums[i]] = i;
        }
        return res;
    }
};

 

3Sum

Given an array nums of n integers, are there elements abc in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

The solution set must not contain duplicate triplets.

Example:

Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路:主要注意的点就是去重问题

时间复杂度:O(N*N)

class Solution {
public:
    vector> res;
    vector> threeSum(vector& nums) {
        if(nums.size() < 3) return res;
        
        sort(nums.begin(),nums.end());
        for(int i = 0;i < nums.size();i++){
            int l = i+1,r = nums.size()-1,sum;
            while(l < r){
                sum = nums[l]+nums[r]+nums[i];
                if(sum == 0){
                    vector out = {nums[i],nums[l],nums[r]};
                    res.push_back(out);
                    // 去重
                    while(l < r && nums[l+1] == nums[l]) l++;
                    while(l < r && nums[r-1] == nums[r]) r--;
                    l++;
                    r--;
                }
                else if(sum < 0) l++;
                else r--;
            }
            // 去重
            while(i < nums.size()-1 && nums[i+1] == nums[i]) i++;
        }
        return res;
    }
};

 

3Sum Closest

Given an array nums of n integers and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

Example:

Given array nums = [-1, 2, 1, -4], and target = 1.

The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

思路:不必考虑去重,相对简单,只需增加判断是否最近即可

class Solution {
public:
    int threeSumClosest(vector& nums, int target) {
        int closest = nums[0] + nums[1] + nums[2];
        int diff = abs(closest - target);
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size() - 2; ++i) {
            int left = i + 1, right = nums.size() - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                int newDiff = abs(sum - target);
                if (diff > newDiff) {
                    diff = newDiff;
                    closest = sum;
                }
                if (sum == target) return target;
                else if (sum < target) ++left;
                else --right;
            }
        }
        return closest;
    }
};

 

4Sum

Given an array nums of n integers and an integer target, are there elements abc, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note:

The solution set must not contain duplicate quadruplets.

Example:

Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.

A solution set is:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

主要注意去重问题

class Solution {
public:
    vector> res;
    vector> fourSum(vector& nums, int target) {
        sort(nums.begin(),nums.end());
        vector out;
        ksum(nums,out,target,4,0,nums.size()-1);
        return res;
    }
    
    void ksum(vector& nums,vector out,int target,int k, int l,int r){
        if(k < 2) return;
        if(k == 2){
            while(l < r){
                int sum = nums[l]+nums[r];
                if(sum == target){
                    out.push_back(nums[l]);
                    out.push_back(nums[r]);
                    res.push_back(out);
                    // 去重
                    while(l < r && nums[l] == nums[l+1]) l++;
                    while(l < r && nums[r] == nums[r-1]) r--;
                    l++;r--;
                    out.pop_back();
                    out.pop_back();
                }
                else if(sum < target) l++;
                else r--;
            }
        }
        else{
            for(int i = l;i <= r-k+1;i++){
                out.push_back(nums[i]);
                ksum(nums,out,target-nums[i],k-1,i+1,r);
                out.pop_back();
                // 去重
                while(i < r && nums[i] == nums[i+1]) i++;
            }
        }
    }
};

 

关于Ksum

2 sum 用hash table做,可以时间O(n),空间O(n)
2 sum 如果用sort以后,在前后扫描,可以时间O(nlogn + n) = O(nlogn),空间O(1)
2 sum 用hash table做的好处是快,但是等于是利用了不用排序的特点。排序的办法,在高维度(也就是k sum问题,k>2)的时候,nlogn就不是主要的时间消耗成分,也就更适合2sum的sort后双指针扫描查找的办法。

那么,对于k sum, k>2的,如果用sort的话, 可以 对 n-2的数做嵌套循环,因为已经sort过了,最后剩下的两维用2 sum的第二个办法, 时间是O(nlogn + n^(k-2) * n) = O(n^(n-1)),空间O(1)。 但是这样跟纯嵌套循环没有什么区别,只是最后一层少了一个因子n。有什么办法能优化?
就是说,对于 k sum (k>2) 问题 (一个size为n的array, 查找k个数的一个tuple,满足总和sum为0), 有没有时间复杂度在O(n^(k-2))的办法?

之前常规的一层一层剥离,n的次数是递增的。只有在最后一层,还有两个维度的时候,时间开销上减少一个n的因子,但是这样时间开销还是太多

我们可以通过对问题分解来解决
举个例子
...-5,-4,-3,-2,-1, 0,1, 2, 3, 4, 5.... 要找 4 sum = 0
那么先分解
4 分成 2 sum + 2 sum 来解决,但是这里的子问题2 sum没有sum=0的要求,是保留任何中间值。只有当子问题的2 sum解决以后,回归原问题的时候,我们才又回归原始的2 sum问题,这时候sum=0
子问题,空间和时间消耗,都是O(n^2)
回归大问题,时间消耗,是O(n^2)

假设k sum中  k = 2^m, 那么一共有m层,会有m次分解
分解到最底层,时间空间消耗 从 原始O(n)变为新的O(n^2)
分解到次底层,时间空间消耗 从 O(n^2)变为新的O((n^2)^2)
...
到达最顶层,时间空间消耗就都变成了O(n^(2*m)) = O(n^(2logk))

和之前的方法O(n^(k-1))相比,O(n^(2logk))的时间是少了很多,但是空间消耗却很大。
因为子问题无法确定把哪一个中间结果留下,那么就需要把子问题的结果全部返回,到最后,空间消耗就很大了。整体效果算是空间换时间吧。

通过 问题的分解 + hashtable的运用,能明显减少时间消耗, 但是空间消耗变大是个问题。比如说,如果有10^6的int类型数组,我如果用这个hashtable的办法,就要有10^12的pair,这就有10T以上的空间消耗。

问题的分解是个很好的思路,但是中间值得保留迫使空间消耗增大,这和用不用hashtable倒没有很大关系,只是说,如果不用hashtable,时间消耗会更大。

 

你可能感兴趣的:(LeetCode刷题记录)