Leetcode——1. 两数之和/15. 三数之和/18. 四数之和/ 454. 四数相加 II

概述

1. 两数之和

15. 三数之和

18. 四数之和

  • 这三题可一起分析这种【多数之和】问题

454. 四数相加 II

  • 四数之和的另一种形式

分析

1

  • 两数之和比较简单,暴力就直接两种循环就可以了,复杂度是 O ( N 2 ) O(N^2) O(N2)

  • 实际上,第二重循环是寻找特定的元素,所以我们可以用一个map,利用key值来记录数组中包含的所有元素,进而可以直接替代循环直接找到所需的元素

  • 当然,我们还可以先将数组排序,然后利用双指针,分别从头、尾开始移动,可以直接确定两个元素;

    但是,排序的复杂度 O ( N l o g N ) O(NlogN) O(NlogN),所以这里不使用

15

  • 三数之和仍然可以使用暴力,三重循环,但是到了三重循环基本上都会超时,所以我们需要观察题目要求简化
  • 首先,示例1告诉我们,存在多个解,并且输入数组中是可以有重复的元素的,所以我们不能想第1题那样使用map来记录
  • 所以,我们考虑上面提到的排序+双指针,这样可以减少两重循环,使得复杂度变成第一种循环+双指针循环 O ( N l o g N + N 2 ) O(NlogN+N^2) O(NlogN+N2)

18

  • 首先,肯定不能暴力解决了,这题实际上方法和第15题一样,也是排序+双指针法

    可能有人会怀疑,即使这样复杂度也是 O ( N 3 ) O(N^3) O(N3),会不会超时

  • 这个时候,我们就需要看一些数据,题目限制了数组长度是200,所以即使是三次方也就 1 0 6 10^6 106,不会超时

  • 但是注意时,这题四数的和不一定是0

    代码中有所体现

454

  • 首先,这题同样是一个四数相加问题,但是这里的输出结果与上面的不同,需要我们输出索引,因为我们不能对输入的数组做任何排序动作,也就无法使用双指针

  • 所以,我们这题只能使用之前的map数组映射

  • 如果只对一个数组二分,最终复杂度是 O ( N 3 ) O(N^3) O(N3),在此题下可以接受;

    但是我们可有选择将数组的和保存,这样复杂度是 O ( N 2 ) O(N^2) O(N2)

    具体见代码注释

思路

1

如何利用map呢?

  • 首先key一定等于数组中的值,这样才能利用key直接确定数组中是否有该值

  • map中的value是否不需要利用呢? 并不是。因为题目要求同一个元素不能重复出现,所以value应该保留key值对应的数组的下标,通过下标确定该元素不是第一重循环此时固定的元素

    这里的前提是因为题目说明只会有一个答案,说明数组中没有重复的值,才可以这样操作;

    如果考虑有两个3,然后我们的目标是6,实际上两个3之和就等于6,但是我们使用map就只能记录一个map,无法获得解答

15

双指针如何确定两个元素并且不重复呢?

  • 利用双指针指向排好序的数组,初始时left指向数组最小值,right指向数组最大值,我们记目标值为target

    • 如果nums[left] + num[right] > target,则需要减少,则right向左移
    • 相反,则left向右移
  • 那么如何确保不重复呢?

    • 首先,因为我们是排好序的数组,所以每次第一轮循环确定了一个元素,然后我们双指针指向的是该元素之后的数组

    • 其次,因为可能存在有多个解,所以我们在双指针时获得一组解后,不能直接退出,应该继续双指针向后,寻找可能不同的第二组解;

      但是,因为有重复的元素,所以可能会找到重复的解

      又因为我们是排序过的,所以重复的值一定在一起,所以我们寻找第二组不同的解是,应该跳过上一组解重复的值(代码中注释)

代码

1

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> unordered_map_int_to_bool;
        for (int i = 0; i < nums.size(); ++i)
            unordered_map_int_to_bool[nums[i]] = i;		// 记录num[i]对应的数组中的索引

        for (int i = 0; i < nums.size(); ++i)		
            if (int j = unordered_map_int_to_bool[target - nums[i]])	// 首先判断该元素是否存在
                if (i != j)		// 接着判断该元素是否和此轮循环的元素重复
                    return {i,j};
        return {};
    }
};

15

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());		// 排序

        vector<vector<int>> result;
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] > 0) {      // 剪枝,因为和是固定0,所以可以这样操作
                return result;
            }
            if (i != 0 && nums[i] == nums[i - 1])		// 跳过重复的值再开始
                continue;
            // 双指针,注意left=i+1
            int left = i + 1, right = nums.size() - 1;
            int target = 0 - nums[i];
            while (left < right) {
                if (target == nums[left] + nums[right]) {
                    result.push_back({nums[i], nums[left], nums[right]});
                    // 跳过重复的值
                    ++left;		// 这里left先++,是为了确保left一定会向前移动,即使没有进入while(也有其他的方法)
                    while(left < nums.size() &&nums[left] == nums[left - 1]) ++left;		// 跳过和该组解重复的值
                    --right;
                    while( right > i + 1 && nums[right] == nums[right + 1])   --right;
                }else if (target > nums[left] + nums[right])
                    ++left;
                else
                    --right;
            }
        }
        return result;
    }
};

18

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());

        vector<vector<int>> result;
        for (int i = 0; i < nums.size(); ++i) {
            // 这里没有剪枝操作了,因为可能和是负数,-4+-2 = -6,但是-4 > -6
            if (i != 0 && nums[i] == nums[i - 1])
                continue;
            for (int j = i + 1; j < nums.size(); ++j) {		// 两重循环
                if (j != i + 1 && nums[j] == nums[j - 1])   
                    continue;

                int left = j + 1, right = nums.size() - 1;		// left从j+1开始
                int target_temp = target - nums[i] - nums[j];
           		// 双指针代码和15 
                while (left < right) {
                    if (target_temp == nums[left] + nums[right]) {
                        result.push_back({nums[i], nums[j], nums[left], nums[right]});
                        ++left;
                        while(left < nums.size() && nums[left] == nums[left - 1]) ++left;
                        --right;
                        while( right > j + 1 && nums[right] == nums[right + 1])   --right;
                    }else if (target_temp > nums[left] + nums[right])
                        ++left;
                    else
                        --right;
                }
            }
        }
        return result;
    }
};

454

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> unordered_map_int_to_int_1;
        for (auto n1 : nums1)
            for (auto n2 : nums2)
                ++unordered_map_int_to_int_1[n1 + n2];     // 会有重复的和,所有需要利用value来保存有几种

        int result = 0;   
        for (auto n3 : nums3)
            for (auto n4 : nums4)
                    result += unordered_map_int_to_int_1 [0 - (n3 + n4)];
        
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值