【力扣】数据之和系列

【力扣】数据之和系列

Leetcode 0015 三数之和

题目描述:Leetcode 0015 三数之和

在这里插入图片描述

分析

  • 本题的考点:双指针

  • 本题的暴力解法是三种循环枚举三个数,时间复杂度是 O ( n 3 ) O(n^3) O(n3)的,不可取。

  • 为了使用双指针,首先对数组进行排序,然后我们可以用i枚举三个数中的其中一个,然后使用双指针j、k找到另外两个数。这里假设i < j < k

  • 对于当前考虑的nums[i],此时可以看成定值,我们初始化j = i+1, k = nums.size()-1,对于nums[j]来说,我们需要找到一个最小的k,使得有 n u m s [ i ] + n u m s [ j ] + n u m s [ k ] ≥ 0 nums[i]+nums[j]+nums[k]\ge 0 nums[i]+nums[j]+nums[k]0

  • 则当j增大时,k必须减小,否则两者都增大的话,三者之和会增大,而我们要找到三者之和为0的数据,不符合要求。这类似于对撞指针

  • 另外还要考虑不能出现重复答案,因为是排序后的数组,当我们遍历到相同元素直接跳过即可。

代码

  • C++
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {

        sort(nums.begin(), nums.end());
        
        vector<vector<int>> res;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] > 0) break;  // 后面两个数一定大于等于nums[i],可以直接退出
            if (i && nums[i] == nums[i - 1]) continue;
            // 双指针算法
            for (int j = i + 1, k = nums.size() - 1; j < k; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= 0) k--;
                if (nums[i] + nums[j] + nums[k] == 0) {
                    res.push_back({nums[i], nums[j], nums[k]});
                }
            }
        }
        return res;
    }
};
  • Java
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {

        Arrays.sort(nums);
        
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) break;  // 后面两个数一定大于等于nums[i],可以直接退出
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            // 双指针算法
            for (int j = i + 1, k = nums.length - 1; j < k; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= 0) k--;
                if (nums[i] + nums[j] + nums[k] == 0) {
                    res.add(get(nums[i], nums[j], nums[k]));
                }
            }
        }
        return res;
    }

    private List<Integer> get(int a, int b, int c) {
        List<Integer> res = new ArrayList<>();
        res.add(a); res.add(b); res.add(c);
        return res;
    }
}
  • Python
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()

        res = []
        for i in range(len(nums)):
            if nums[i] > 0:
                break
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            j = i + 1; k = len(nums) - 1
            while j < k:
                if j > i + 1 and nums[j] == nums[j - 1]:
                    j += 1
                    continue
                while j < k - 1 and nums[i] + nums[j] + nums[k - 1] >= 0:
                    k -= 1
                if nums[i] + nums[j] + nums[k] == 0:
                    res.append([nums[i], nums[j], nums[k]])
                j += 1
        return res

时空复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)n为数组长度。因为外层循环中每次循环都会执行一次双指针算法,双指针算法时间复杂度是 O ( n ) O(n) O(n)的,因此总体时间复杂度为 O ( n 2 ) O(n^2) O(n2)的。

  • 空间复杂度: O ( 1 ) O(1) O(1)

Leetcode 0016 最接近的三数之和

题目描述:Leetcode 0016 最接近的三数之和

在这里插入图片描述

分析

  • 本题的考点:双指针

  • 本题的暴力解法是三种循环枚举三个数,时间复杂度是 O ( n 3 ) O(n^3) O(n3)的,不可取。

  • 为了使用双指针,首先对数组进行排序,然后我们可以用i枚举三个数中的其中一个,然后使用双指针j、k找到另外两个数。这里假设i < j < k

  • 对于当前考虑的nums[i],此时可以看成定值,我们初始化j = i+1, k = nums.size()-1,对于nums[j]来说,我们需要找到一个最小的k,使得有 n u m s [ i ] + n u m s [ j ] + n u m s [ k ] ≥ t a r g e t nums[i]+nums[j]+nums[k]\ge target nums[i]+nums[j]+nums[k]target;因为k是最小的一个,所以一定有 n u m s [ i ] + n u m s [ j ] + n u m s [ k − 1 ] < t a r g e t nums[i]+nums[j]+nums[k-1] < target nums[i]+nums[j]+nums[k1]<target。这样最接近target的两种情况都能枚举到。

  • 则当j增大时,k必须减小,否则两者都增大的话,三者之和会增大,而我们要找到三者之和为0的数据,不符合要求。这类似于对撞指针

  • 因为我们要存储当前三数之和与target的差值以及三数之和,因此可以使用pair

代码

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

        pair<int, int> res(INT_MAX, INT_MAX);  // (三数之和与target的差值的绝对值, 三数之和)
        for (int i = 0; i < nums.size(); i++) 
            for (int j = i + 1, k = nums.size() - 1; j < k; j++) {
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= target) k--;
                int s = nums[i] + nums[j] + nums[k];
                res = min(res, make_pair(abs(s - target), s));
                if (j < k - 1) {
                    s = nums[i] + nums[j] + nums[k - 1];
                    res = min(res, make_pair(abs(s - target), s));
                }
            }
        return res.second;
    }
};
  • Java
class Solution {
    static class MyPair {
        int x, y;
        public MyPair(int x, int y) {
            this.x = x; this.y = y;
        }
    }

    public int threeSumClosest(int[] nums, int target) {

        Arrays.sort(nums);

        MyPair res = new MyPair(Integer.MAX_VALUE, Integer.MAX_VALUE);
        for (int i = 0; i < nums.length; i++) 
            for (int j = i + 1, k = nums.length - 1; j < k; j++) {
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= target) k--;
                int s = nums[i] + nums[j] + nums[k];
                if (Math.abs(s - target) < res.x) {
                    res.x = Math.abs(s - target);
                    res.y = s;
                }
                if (j < k - 1) {
                    s = nums[i] + nums[j] + nums[k - 1];
                    if (Math.abs(s - target) < res.x) {
                        res.x = Math.abs(s - target);
                        res.y = s;
                    }
                }
            }
        return res.y;
    }
}
  • Python
class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        res = [1e9, 1e9]
        for i in range(len(nums)):
            j = i + 1; k = len(nums) - 1
            while j < k:
                while j < k - 1 and nums[i] + nums[j] + nums[k - 1] >= target:
                    k -= 1
                s = nums[i] + nums[j] + nums[k]
                res = min(res, [abs(s - target), s])
                if j < k - 1:
                    s = nums[i] + nums[j] + nums[k - 1]
                    res = min(res, [abs(s - target), s])
                j += 1
        return int(res[1])

时空复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)n为数组长度。因为外层循环中每次循环都会执行一次双指针算法,双指针算法时间复杂度是 O ( n ) O(n) O(n)的,因此总体时间复杂度为 O ( n 2 ) O(n^2) O(n2)的。

  • 空间复杂度: O ( 1 ) O(1) O(1)

Leetcode 0018 四数之和

题目描述:Leetcode 0018 四数之和

在这里插入图片描述

分析

  • 本题的考点:双指针

  • 这一题和Leetcode 0015 三数之和的解法一样,只不过需要枚举两个元素,另外两个元素使用双指针寻找。

代码

  • C++
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {

        sort(nums.begin(), nums.end());

        vector<vector<int>> res;
        for (int i = 0; i < nums.size(); i++) {
            if (i && 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;
                // 双指针算法
                for (int k = j + 1, u = nums.size() - 1; k < u; k++) {
                    if (k > j + 1 && nums[k] == nums[k - 1]) continue;
                    while (k < u - 1 && nums[i] + nums[j] + nums[k] + nums[u - 1] >= target) u--;
                    if (nums[i] + nums[j] + nums[k] + nums[u] == target) {
                        res.push_back({nums[i], nums[j], nums[k], nums[u]});
                    }
                }
            }
        }
        return res;
    }
};
  • Java
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {

        Arrays.sort(nums);

        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            for (int j = i + 1; j < nums.length; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                for (int k = j + 1, u = nums.length - 1; k < u; k++) {
                    if (k > j + 1 && nums[k] == nums[k - 1]) continue;
                    while (k < u - 1 && nums[i] + nums[j] + nums[k] + nums[u - 1] >= target) u--;
                    if (nums[i] + nums[j] + nums[k] + nums[u] == target) {
                        res.add(get(nums[i], nums[j], nums[k], nums[u]));
                    }
                }
            }
        }
        return res;
    }

    private List<Integer> get(int a, int b, int c, int d) {
        List<Integer> res = new ArrayList<>();
        res.add(a); res.add(b); res.add(c); res.add(d);
        return res;
    }
}
  • Python
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()

        res = []
        for i in range(len(nums)):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, len(nums)):
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                k = j + 1; u = len(nums) - 1
                while k < u:
                    if k > j + 1 and nums[k] == nums[k - 1]:
                        k += 1
                        continue
                    while k < u - 1 and nums[i] + nums[j] + nums[k] + nums[u - 1] >= target:
                        u -= 1
                    if nums[i] + nums[j] + nums[k] + nums[u] == target:
                        res.append([nums[i], nums[j], nums[k], nums[u]])
                    k += 1
        return res

时空复杂度分析

  • 时间复杂度: O ( n 3 ) O(n^3) O(n3)。两重循环+双指针。

  • 空间复杂度: O ( 1 ) O(1) O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值