【专题刷题】双指针(三):两数之和,三数之和,四数之和

📝前言说明:

  • 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing
📋本专栏:C++刷题专栏
📋其他专栏:C语言入门基础python入门基础C++学习笔记Linux
🎀CSDN主页 愚润泽


167. 两数之和 II

在这里插入图片描述

个人解

思路:
暴力求解的时间复杂度:O($n^2$)每次移动只能获取一个信息,移动 n 2 n^2 n2 次,才能得到所有两数相加的结果。

那使用双指针呢?

使用双指针,利用数组的非递减性(也就是非严格单调递增性)
我们让一个指针指向左边最小的元素,一个指针指向右边最大的元素
要寻找numbers[left] + numbers[right] == target
numbers[left] + numbers[right] > target的时候为例,让right--,移动一次得到n个信息,因为当最小数和numbers[right]相加都大于target了,那其他numbers[left]之后更大的数与numbers[right]相加就没有意义了,一下可以排除n个数。left++也同理

用时:3:00
屎山代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 1, right = numbers.size();
        while(left < right)
        {
            int sum = numbers[left - 1] + numbers[right - 1];
            if(sum < target)
                left++;
            else if(sum > target)
                right--;
            else
                return {left, right};
        }
        return {-1,-1};
    }
};

时间复杂度:O(n)
空间复杂度:O(1)


优质解

简单题简单做,双指针已够用


15. 三数之和

在这里插入图片描述

个人解

思路:用base遍历整个数组,作为基准,将问题转换成nums[left] + nums[right] = 0 - nums[base]的问题。然后基于这个标准(nums[left] + nums[right] = 0 - nums[base]),再利用数组的单调性,再次用双指针。

用时:20:00(为什么这么长,因为题目要求不能重复,被去重坑惨了)
屎山代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        ranges::sort(nums);
        int n = nums.size(), base = 0;
        vector<vector<int>> ans;
        for(; base < n - 2; base++)
        {
            int left = base + 1, right = n - 1;
            while(left < right)
            {
                if(nums[base] + nums[left] + nums[right] < 0)
                    left++;
                else if(nums[base] + nums[left] + nums[right] > 0)
                    right--;
                else
                {
                    ans.push_back({nums[base], nums[left], nums[right]});
                    while(left < right && nums[left + 1] == nums[left])
                        left++;
                    while(left < right && nums[right - 1] == nums[right])
                        right--;
                    left++;
                    right--;
                }
            }
            while(base < n - 3 && nums[base + 1] == nums[base])
                base++;
        }
        return ans;
    }
};

时间复杂度:O( n 2 n^2 n2)
空间复杂度:O(1)


优质解

思路:双指针已经够用
但是我们来欣赏一下别人处理重复元素的思路,以及其他优化,和优雅的代码
代码(来自Leetcode上灵茶山艾府):

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        ranges::sort(nums);
        vector<vector<int>> ans;
        int n = nums.size();
        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];
            // 跳过重复数字,base在使用前确保是唯一的,数组中无重复
            if (i && x == nums[i - 1]) continue; 
            // 优化一(加上两个最小数)
            if (x + nums[i + 1] + nums[i + 2] > 0) break; 
            // 优化二(加上两个最大数)
            if (x + nums[n - 2] + nums[n - 1] < 0) continue; 
            int j = i + 1, k = n - 1;
            while (j < k) {
                int s = x + nums[j] + nums[k];
                if (s > 0) {
                    k--;
                } else if (s < 0) {
                    j++;
                } else { // 三数之和为 0
                    ans.push_back({x, nums[j], nums[k]});
                    // 跳过重复数字(用 for 可以多跳一步,直接跳到不相等的下一个元素)
                    for (j++; j < k && nums[j] == nums[j - 1]; j++); 
                    for (k--; k > j && nums[k] == nums[k + 1]; k--);
                }
            }
        }
        return ans;
    }
};

18. 四数之和

个人解

在这里插入图片描述
思路:借助三数之和的解决方法,定好一个base以后又转换成三数之和的问题。
不过这时候优化的地方有两个,因为其实可以看做是有两个base
用时:19:00

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        ranges::sort(nums);
        vector<vector<int>> ans;
        int n = nums.size();
        for(int base = 0; base < n - 3; base++)
        {   // 错误示范:应该要先用完再去除,因为万一其他数用的和base一样呢?
            // if(base < n - 4 && nums[base + 1] == nums[base])
            //     continue; // 去除重复元素
            if(base && nums[base] == nums[base - 1])
                continue;
            long long x = nums[base]; // 这题老六 数字会溢出
            if(x + nums[base + 1] + nums[base + 2] + nums[base + 3] > target)
                break;
            if(x + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) 
                continue;
            for(int i = base + 1; i < n - 2; i++)
            {
                if(i > base + 1 && nums[i] == nums[i - 1])
                    continue;
                long long y = nums[i];
                if(x + y + nums[i + 1] + nums[i + 2] > target)
                    break;
                if(x + y + nums[n - 2] + nums[n - 1] < target) 
                    continue;
                int j = i + 1, k = n - 1;
                while(j < k)
                {
                    if(x + y + nums[j] + nums[k] < target)
                        j++;
                    else if(x + y + nums[j] + nums[k] > target)
                        k--;
                    else
                    {
                        ans.push_back({(int)x, (int)y, nums[j], nums[k]});
                        for(j++; j < k && nums[j] == nums[j - 1]; j++);
                        for(k--; j < k && nums[k] == nums[k + 1]; k--);
                    }
                }
            }
        }
        return ans;
    }
};

时间复杂度:O( n 3 n^3 n3)
空间复杂度:O(1)

注意跳过重复元素:必须是用完以后base再跳过重复元素,因为有可能后面的数可能取和base一样的值,但是被base跳过了就取不到了。

优质解

双指针已足以,暂时不探索其他解


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚润泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值