三数之和-双指针15-python&C++

本文介绍了如何利用双指针技巧减少三数之和问题的循环次数,通过先排序数组避免重复,然后固定一个数,利用双指针在剩余数中查找和为目标值的组合。此外,还展示了两种不同的Python实现方法及其时间复杂度分析。
摘要由CSDN通过智能技术生成

算法思想:

1.a+b+c=0,自由度为2,所以可以不用三循环,而使用双循环;
2.先给数组从小到大排序,即保证a≤b≤c,从而避免答案出现[a,b,c],[b,c,a]…等重复数组;
3.固定a后,寻找b,c的过程可以利用双指针,b和c分别从前往后和从后往前枚举,从而避免重复查找。

灵感:当出现互为限制的两个变量(即自由度为1)的查找问题时,可以考虑利用双指针。

python

class Solution:
    def threeSum(self, nums):
        n = len(nums)
        nums.sort() # 为避免重复输出,从小到大对输入数组排序
        ans = list()
        for first in range(n): # a指针,从前往后
            if first > 0 and nums[first] == nums[first-1]: 
            # 避免本次枚举数字的上次一样,从而避免重复
                continue
            third = n-1 # c指针从后往前,从而将三个循环减少为两个
            target = 0 - nums[first]
            for second in range(first+1,n): # b指针,从前往后
                if second > first+1 and nums[second] == nums[second-1]:
                # 避免本次枚举数字的上次一样,从而避免重复
                    continue
                while second < third and nums[second] + nums[third] > target: 
                # b指针要在c指针左侧,避免重复查找
                ''' 由于nums排序为从小到大,故 nums[second] + nums[third] > target 
                可以判断b,c是否符合条件 '''
                    third -= 1
                if second == third: 
                    break # 第二个循环停止
                if nums[second] + nums[third] == target:
                    ans.append([nums[first], nums[second], nums[third]])
        return ans
  • 用二数之和做递归可以解决所有Nsum问题!
class Solution:
    def threeSum(self, nums: List[int]):
        nums = sorted(nums)
        target = 0

        def N_Sum(nums, n, start, target):
            length = len(nums)
            res = []

            if n < 2 or length < n:
                return res
            
            if n == 2:
                l, r = start, length - 1
                
                while l < r:
                    left, right = nums[l], nums[r]
                    sum = nums[l] + nums[r]

                    if sum == target:
                        res.append([left, right])
                        while l < r and nums[l] == left:
                            l += 1
                        while l < r and nums[r] == right:
                            r -= 1
                    elif sum < target:
                        while l < r and nums[l] == left:
                            l += 1
                    else:
                        while l < r and nums[r] == right:
                            r -= 1
            else:
                i = start
                while i < length:
                    sub = N_Sum(nums, n-1, i+1, target-nums[i])

                    for arr in sub:
                        arr.insert(0, nums[i])
                        res.append(arr)
                    
                    while i < length - 1 and nums[i] == nums[i+1]:
                        i += 1
                    i += 1
            
            return res
        
        return N_Sum(nums, 3, 0, target)

C++

class Solution {
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        
        vector<vector<int>> res;
        if(num.size() < 3) return res;
        
        sort(num.begin(), num.end());
        for(int i = 0; i < num.size() - 2; i++){
            int j = i + 1;
            int k = num.size() - 1;
            int target = -num[i];
            while(j < k){
                if(num[j] + num[k] > target) k--;
                else if(num[j] + num[k] < target) j++;
                else{
                    vector<int> curr = {num[i], num[j], num[k]};
                    res.push_back(curr);
                    while(j + 1 < k && num[j] == num[j + 1]) j++;
                    while(k - 1 > j && num[k] == num[k - 1]) k--;
                    j++, k--;
                }
            }
            while(i + 1 < num.size() - 2 && num[i] == num[i + 1]) i++;
        }
        return res;
    }
};
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        // 先排序,以防止重复结果
        sort(nums.begin(), nums.end());
        return nSum(nums, 3, 0, 0);
    }

    vector<vector<int>> nSum(vector<int>& nums, int n, int start, int target) {
        // 实现了一个通用递归函数求nSum问题
        int length = nums.size();
        vector<vector<int>> res;
        // 特殊情况跳出
        if (n < 2 || length < n) return res;

        //n==2是basecase
        if (n == 2) {
            int left = start, right = length - 1;
            while (left < right) {
                int lo = nums[left], hi = nums[right];
                int tmp = lo + hi;
                // 以下while循环都是在跳过重复元素
                if (tmp == target) {
                    res.emplace_back(vector<int>{nums[left], nums[right]});
                    while (left < right && nums[left] == lo) left++;
                    while (left < right && nums[right] == hi) right--;
                } else if (tmp < target) {
                    while (left < right && nums[left] == lo) left++;
                } else {
                    while (left < right && nums[right] == hi) right--;
                }
            }
        } else {
            // n>2 需要递归到n-1数之和问题
            for (int i = start; i < length; i++) {
                vector<vector<int>> arrs = nSum(nums, n-1, i+1, target-nums[i]);
                for (auto& arr : arrs) {
                    arr.emplace_back(nums[i]);
                    res.emplace_back(arr);
                }
                // 跳过重复元素
                while (i < length - 1 && nums[i] == nums[i+1]) i++;
            }
        }

        return res;
    }
};

复杂度分析:

  • 时间复杂度:O(N2),其中 N是数组nums 的长度。
  • 空间复杂度:O(logN),我们忽略存储答案的空间,额外的排序的空间复杂度为O(logN)。然而我们修改了输入的数组nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了nums 的副本并进行排序,空间复杂度为 O(N)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值