[leetcode] 1. twoSum; 167. Two Sum II - Input array is sorted; 15. threeSum; 16. 3Sum Closest; 18. 4

这篇文章涉及到leetcode中求和的相关题目。

1. twoSum [Easy]

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].

对于这道题目,最简单的方法就是采用两重循环暴力破解,复杂度为 O(n2) 。虽然这种方法也可以AC,但是当输入数组的长度太大就会花费很多时间。我的方法是使用哈希表,预先存储每个数字对应的下标,一次遍历输入数组,另外一个数字使得相加的和为目标数,时间复杂度为 O(n)

vector<int> twoSum(vector<int>& nums, int target) {
    unordered_map<int, int> num2ind;
    for (int i = 0; i < nums.size(); ++i)
    {
        num2ind[nums[i]] = i;
    }

    vector<int> result;
    for (int i = 0; i < nums.size(); ++i)
    {
        const int gap = target - nums[i];
        if (num2ind.find(gap) != num2ind.end() && num2ind[gap] > i) {
            result.push_back(i);
            result.push_back(num2ind[gap]);
            break;
        }
    }
    return result;
}

167. Two Sum II - Input array is sorted [Easy]

Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution and you may not use the same element twice.

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

这题与上一题不同的地方在于输入的整数是按照升序排列的,因此可以采取左右夹逼的办法,时间复杂度为 O(n)

vector<int> twoSum(vector<int>& numbers, int target) {

    vector<int> result;
    int pre = 0;
    int post = numbers.size()-1;
    while(pre < post) {
        int sum = numbers[pre] + numbers[post];
        if (sum == target) {
            result.push_back(pre+1);
            result.push_back(post+1);
            break;
        } else if (sum < target) pre += 1;
        else post -=  1;
    }
    return result;
}

15. 3Sum [Medium]

Given an array S of n integers, are there elements a, b, c in S 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.

For example, given array S = [-1, 0, 1, 2, -1, -4],

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

这一题需要找出三个整数,使得其和等于0。可以采用求两个数和的方法,先对整数进行排序,然后采取左右夹逼的方法找出结果。时间复杂度为 O(n2) 。但是由于输入的整数可能存在重复的数字,因此得到的解会有重复,需要采取一定的方法将重复解去掉。一种最简单的方法就是每次找到一个解后,去查看这个解是否存在于已经找出的解集中。这种方法思想简单,实现也很简单,但是非常耗费时间。因此我采取的方法是:在固定某个数,查找与其搭配的数之前,先判断当前这个数字是否与其旁边的数相同,如果相同说明已经对同样地数字查找过了,可以直接跳过。比如已经固定了第一个数nums[first], 由于数字已经排好序,并且first是从左到右遍历的,因此需要判断nums[first]是否与nums[first-1]相同。但是由于first从0开始,需要还需要判断first是否大于0,因此判断的方法就是:

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

同样的,对于第二个数和第三个数也需要进行判断:

do{second++;} while (second < third && nums[second] == nums[second-1]); 
do{third--;} while (second < third && nums[third] == nums[third+1]); 

通过上述判断,可以将重复的解去掉了。但是我们还可以提升一下程序的速度。由于整数是已经排好序的,因此如果我们发现最小的三个数的和比0要大,则不需要继续判断,即:

for (int first = 0; first < n-2; ++first)
{
    if (nums[first] + nums[first+1] + nums[first+2] > 0) break;
}

同样地,如果我们发现nums[first]与最大的两个数之和比0要小,说明nums[first]与其他数的和不可能为0,可以直接跳过当前的nums[first],即:

if (nums[first] + nums[n-2] + nums[n-1] < 0) continue;

完整的代码如下:

vector<vector<int> > threeSum(vector<int>& nums) {

    vector<vector<int> > result;
    int n = nums.size();
    if (n < 3)
        return result;

    sort(nums.begin(), nums.end());
    for (int first = 0; first < n-2; ++first)
    {
        //skip same value
        if (first > 0 && nums[first] == nums[first-1]) continue; 
        //smallest value > target
        if (nums[first] + nums[first+1] + nums[first+2] > 0) break; 
        // biggest value < target
        if (nums[first] + nums[n-2] + nums[n-1] < 0) continue;
        int second = first+1;
        int third = n-1;
        while (second < third) {
            int sum = nums[first] + nums[second] + nums[third];
            if (sum == 0) {
                result.push_back(vector<int>{nums[first], nums[second], nums[third]});
                // skip same value
                do{second++;} while (second < third && nums[second] == nums[second-1]); 
                do{third--;} while (second < third && nums[third] == nums[third+1]); 
            } else if (sum < 0) {
                // skip same value
                do{second++;} while (second < third && nums[second] == nums[second-1]); 
            } else 
                // skip same value
                do{third--;} while (second < third && nums[third] == nums[third+1]);
        }
    }
    return result;
}

16. 3Sum Closest [Medium]

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

For example, given array S = {-1 2 1 -4}, and target = 1.

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

这一题其实与求三个数的和的思想相似,同样可以先对整数排序,然后采取左右夹逼的方法找到目标数字。在遍历的过程中,如果找到了目标和则直接返回,否则判断当前数字之和、当前最近的和两者与目标和的距离谁更小来更新最近和即可。

int threeSumClosest(vector<int>& nums, int target) {
    if(nums.size() < 3) return 0;
    sort(nums.begin(),  nums.end());
    int close_sum = nums[0] + nums[1] + nums[2];
    for (int i = 0; i < nums.size()-2; ++i)
    {
        if (i > 0 && nums[i] == nums[i-1]) continue;
        int j = i+1;
        int k = nums.size()-1;
        while(j < k) {
            int cur_sum = nums[i] + nums[j] + nums[k];
            if (cur_sum == target) return cur_sum; // no need to find numbers suming to target
            else if (cur_sum < target) j++;
            else k--;

            if (abs(cur_sum - target) < abs(close_sum - target))
                close_sum = cur_sum;
        }
    }
    return close_sum;
}

18 4Sum

Given an array S of n integers, are there elements a, b, c, and d in S 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.

For example, given array S = [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]
]

这题可以采取第15题的方法,先对数据进行排序,然后依次固定第一、第二个数,并且对剩下的数字左右夹逼求解:

vector<vector<int> > fourSum(vector<int>& nums, int target) {

    vector<vector<int> > result;
    int n = nums.size();
    if (n < 4) return result;

    sort(nums.begin(), nums.end());
    for (int first = 0; first < n-3; ++first)
    {
        if (first > 0 && nums[first] == nums[first-1]) continue; // skip same value
        if (nums[first] + nums[first+1] + nums[first+2] + nums[first+3] > target)
            break;
        if (nums[first] + nums[n-3] + nums[n-2] + nums[n-1] < target)
            continue;
        for (int second = first+1; second < n-2; ++second)
        {
            if (second > first+1 && nums[second] == nums[second-1])
                continue;
            if (nums[first] + nums[second] 
                + nums[second+1] + nums[second+2] > target)
                break;
            if (nums[first] + nums[second] + nums[n-2] + nums[n-1] < target)
                continue;
            int third = second+1;
            int four = n-1;
            int sub_target = target - nums[first] - nums[second];
            while(third < four) {
                int sum = nums[third] + nums[four];
                if (sum == sub_target) {
                    result.push_back(vector<int>{nums[first], nums[second], nums[third], nums[four]});
                    do{third++;} while(third < four && nums[third] == nums[third-1]); 
                    do{four--;} while(third < four && nums[four] == nums[four+1]); 
                } else if (sum < sub_target) {
                    do{third++;} while(third < four && nums[third] == nums[third-1]);
                } else {
                    do{four--;} while(third < four && nums[four] == nums[four+1]);
                }
            }
        }
    }
    return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值