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<int> twoSum(vector<int>& nums, int target) {
        vector<int> res;
        if(nums.size() < 2) return res;

        // 空间换时间
        map<int,int> 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<int> twoSum(vector<int>& nums, int target) {
        vector<int> res;
        if(nums.size() < 2) return res;

        // 直接边插入边查找,效率更高
        map<int,int> 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<vector<int>> res;
    vector<vector<int>> threeSum(vector<int>& 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<int> 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<int>& 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<vector<int>> res;
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<int> out;
        ksum(nums,out,target,4,0,nums.size()-1);
        return res;
    }
    
    void ksum(vector<int>& nums,vector<int> 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,时间消耗会更大。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值