大厂算法指南:优选算法 ——双指针篇(下)

简介: 大厂算法指南:优选算法 ——双指针篇(下)

📕作者简介:非科班在读,纯技术博客分享,致力于C/C++,涉及Python、C/C++、Linux,数据结构,git企业级开发,Mysql等。

📗本文收录于算法指南,旨在帮助读者应对各大互联网大厂笔试题,构建完整的算法体系!

📘相关专栏C语言C++数据结构Linux前言科技喜欢C/C++/Linux/算法的朋友们可以关注一下哦!

前言:双指针简介

常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。

对撞指针:⼀般⽤于顺序结构中,也称左右指针。

。对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。

。对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:

◦ left == right (两个指针指向同⼀个位置)

◦ left > right (两个指针错开)

快慢指针:⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。

这种⽅法对于处理环形链表或数组⾮常有⽤。其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。

快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:

。 在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。

话不多说,现在来看看大厂们比较有代表性的面试题吧!

一、611. 有效三角形的个数

1.1 算法思路(排序 + 双指针)

先将数组排序。

我们可以固定⼀个「最⻓边」,然后在⽐这条边⼩的有序数组中找出⼀个⼆元组,使这个⼆元组之和⼤于这个最⻓边。由于数组是有序的,我们可以利⽤「对撞指针」来优化。


  • 设最⻓边枚举到 i 位置,区间 [left, right] 是 i 位置左边的区间(也就是⽐它⼩的区间):
  • 如果 nums[left] + nums[right] > nums[i] ;说明 [left, right - 1] 区间上的所有元素均可以与 nums[right] 构成⽐nums[i] ⼤的⼆元组,满⾜条件的有 right - left 种。此时 right 位置的元素的所有情况相当于全部考虑完毕, right-- ,进⼊下⼀轮判断。
  • 如果 nums[left] + nums[right] <= nums[i] ;说明 left 位置的元素是不可能与 [left + 1, right] 位置上的元素构成满⾜条件的⼆元组。left 位置的元素可以舍去, left++ 进⼊下轮循环。

1.2 代码实现

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());//排序
        int ret=0;
        for(int i=nums.size()-1; i>=2; i--)
        {
            int left=0, right=i-1, target=nums[i];
            while(left<right)
            {
                if(nums[left] + nums[right] > target)
                {
                    ret += right-left;
                    right--;
                }
                else
                {
                    left++;
                }
            }
        }
        return ret;
    }
};

二、LCR 179. 查找总价格为目标值的两个商品

(https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/)

2.1 算法思路

注意到本题是升序的数组,因此可以⽤「对撞指针」优化时间复杂度。

算法流程如下:

  • 初始化 left , right 分别指向数组的左右两端(这⾥不是我们理解的指针,⽽是数组的下标)
  • 当 left < right 的时候,⼀直循环以下过程:
  • 1.当 nums[left] + nums[right] == target 时,说明找到结果,记录结果,并且返回;
  • 2.当 nums[left] + nums[right] < target 时:
  • 对于 nums[left] ⽽⾔,此时 nums[right] 相当于是 nums[left] 能碰到的最⼤值(别忘了,这⾥是升序数组哈~)。如果此时不符合要求,说明在这个数组⾥⾯,没有别的数符合 nums[left] 的要求了(最⼤的数都满⾜不了你,你已经没救了)。因此,我们可以⼤胆舍去这个数,让 left++ ,去⽐较下⼀组数据;
  • 那对于 nums[right] ⽽⾔,由于此时两数之和是⼩于⽬标值的, nums[right]还可以选择⽐ nums[left] ⼤的值继续努⼒达到⽬标值,因此 right 指针我们按兵不动;
  • 当 nums[left] + nums[right] > target 时,同理我们可以舍去nums[right] (最⼩的数都满⾜不了你,你也没救了)。让 right-- ,继续⽐较下⼀组数据,⽽ left 指针不变(因为他还是可以去匹配⽐ nums[right] 更⼩的数)。

2.2 代码实现

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

三、15. 三数之和

3.1 算法思路

本题与两数之和类似,是⾮常经典的⾯试题。


与两数之和稍微不同的是,题⽬中要求找到所有「不重复」的三元组。那我们可以利⽤在两数之和那⾥⽤的双指针思想,来对我们的暴⼒枚举做优化:

  1. 先排序;
  2. 然后固定⼀个数 a :
  3. 在这个数后⾯的区间内,使⽤「双指针算法」快速找到两个数之和等于 -a 即可。

但是要注意的是,这道题⾥⾯需要有「去重」操作~

  1. 找到⼀个结果之后, left 和 right 指针要「跳过重复」的元素;’
  2. 当使⽤完⼀次双指针算法之后,固定的 a 也要「跳过重复」的元素

3.2 代码实现

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>>  ret;
        //1. 排序
        sort(nums.begin(), nums.end());
        //2. 双指针思路
        for(int i=nums.size()-1; i>=2; i--)
        {
            if(nums[i]<0)
                break;
            int left =0, right=i-1;
            while(left<right)
            {
                if(nums[left] + nums[right] + nums[i] > 0)
                    right--;
                else if(nums[left] + nums[right] + nums[i] < 0)
                    left++;
                else
                {
                    //相等,记录数据;处理相同元素
                    ret.push_back({nums[left], nums[right], nums[i]});
                    left++, right--;
                    //处理相同元素
                    while(left<right && nums[left-1] == nums[left])
                    {
                        left++;
                    }
                    while(left<right && nums[right+1] == nums[right])
                    {
                        right--;
                    }
                }
                //处理相同元素,防止重叠
                while(i >=2 && nums[i] == nums[i-1])
                {
                    i--;
                }
            }
        }
        return ret;
    }
};

四、18. 四数之和

4.1 算法思路(排序 + 双指针)

a. 依次固定⼀个数 a ;

b. 在这个数 a 的后⾯区间上,利⽤「三数之和」找到三个数,使这三个数的和等于 target - a 即可。

4.2 代码实现

lass Solution
{
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        vector<vector<int>> ret;
        // 1. 排序
        sort(nums.begin(), nums.end());
        // 2. 利⽤双指针解决问题
        int n = nums.size();
        for(int i = 0; i < n; ) // 固定数 a
        {
            // 利⽤ 三数之和
            for(int j = i + 1; j < n; ) // 固定数 b
            {
                // 双指针
                int left = j + 1, right = n - 1;
                long long aim = (long long)target - nums[i] - nums[j];
            while(left < right)
            {
                int sum = nums[left] + nums[right];
                if(sum < aim) 
                    left++;
                else if(sum > aim)
                     right--;
                else
                {
                    ret.push_back({nums[i], nums[j], nums[left++], nums[right--]});
                    // 去重⼀
                    while(left < right && nums[left] == nums[left - 1]) 
                        left++;
                    while(left < right && nums[right] == nums[right + 1]) 
                        right--;
                }
            }
            // 去重⼆
            j++;
            while(j < n && nums[j] == nums[j - 1]) 
                j++;
            }
            // 去重三
            i++;
            while(i < n && nums[i] == nums[i - 1]) 
                i++;
        }
        return ret;
    }
};


相关文章
|
16天前
|
算法
【优选算法专栏】专题九:链表--------两两交换链表中的节点
【优选算法专栏】专题九:链表--------两两交换链表中的节点
17 0
|
5天前
|
算法 前端开发 JavaScript
< 每日算法:一文带你认识 “ 双指针算法 ” >
`双指针`并非指的是一种具体的公式或者范式。而是一种运算思路,用于节省逻辑运算时间的`逻辑思路`!双指针算法通常用于`优化时间复杂度`!
< 每日算法:一文带你认识 “ 双指针算法 ” >
|
15天前
|
算法
优选算法|【双指针】|202.快乐数
优选算法|【双指针】|202.快乐数
|
16天前
|
机器学习/深度学习 算法
【优选算法专栏】专题四:前缀和(二)
【优选算法专栏】专题四:前缀和(二)
21 1
|
16天前
|
算法
【优选算法专栏】专题一:双指针--------1.移动0
【优选算法专栏】专题一:双指针--------1.移动0
19 0
|
1月前
|
传感器 算法 计算机视觉
基于肤色模型和中值滤波的手部检测算法FPGA实现,包括tb测试文件和MATLAB辅助验证
该内容是关于一个基于肤色模型和中值滤波的手部检测算法的描述,包括算法的运行效果图和所使用的软件版本(matlab2022a, vivado2019.2)。算法分为肤色分割和中值滤波两步,其中肤色模型在YCbCr色彩空间定义,中值滤波用于去除噪声。提供了一段核心程序代码,用于处理图像数据并在FPGA上实现。最终,检测结果输出到&quot;hand.txt&quot;文件。
|
1月前
|
机器学习/深度学习 算法 计算机视觉
基于yolov2深度学习网络的视频手部检测算法matlab仿真
基于yolov2深度学习网络的视频手部检测算法matlab仿真
|
1月前
|
算法
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
23 2
|
1月前
|
算法
【MATLAB】语音信号识别与处理:一维信号NLM非局部均值滤波算法去噪及谱相减算法呈现频谱
【MATLAB】语音信号识别与处理:一维信号NLM非局部均值滤波算法去噪及谱相减算法呈现频谱
40 1