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

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

  • 前言:双指针简介
  • 一、[283.移动零](https://leetcode.cn/problems/move-zeroes/)
    • 1.1 算法思想(快排的思想:数组划分区间 - 数组分两块)
    • 1.2 算法流程
    • 1.3 代码实现
  • 二、[1089. 复写零](https://leetcode.cn/problems/duplicate-zeros/)
    • 2.1 算法思路
    • 2.2 算法流程
    • 2.3 代码实现
  • 三、[202. 快乐数](https://leetcode.cn/problems/happy-number/)
    • 3.1 算法思路(快慢指针)
    • 3.2 代码实现
  • 四、[11. 盛最多水的容器](https://leetcode.cn/problems/container-with-most-water/)
    • 4.1 算法思路(对撞指针)
    • 代码实现

在这里插入图片描述

📕作者简介:非科班在读,纯技术博客分享,致力于C/C++,涉及Python、C/C++、Linux,数据结构,git企业级开发,Mysql等。
📗本文收录于算法指南,旨在帮助读者应对各大互联网大厂笔试题,构建完整的算法体系!
📘相关专栏C语言、C++、数据结构、Linux、前言科技喜欢C/C++/Linux/算法的朋友们可以关注一下哦!
————————————————
版权声明:本文为CSDN博主「小宇成长录」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://editor.csdn.net/md?not_checkout=1&spm=1011.2124.3001.6183&articleId=134906130

前言:双指针简介

常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。
对撞指针:⼀般⽤于顺序结构中,也称左右指针。

。对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。
。对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:
◦ left == right (两个指针指向同⼀个位置)
◦ left > right (两个指针错开)

快慢指针:⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
这种⽅法对于处理环形链表或数组⾮常有⽤。其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。
快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:

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

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


一、283.移动零

「数组分两块」是⾮常常⻅的⼀种题型,主要就是根据⼀种划分⽅式,将数组的内容分成左右两部
分。这种类型的题,⼀般就是使⽤「双指针」来解决。

题目描述:
在这里插入图片描述

1.1 算法思想(快排的思想:数组划分区间 - 数组分两块)

在本题中,我们可以⽤⼀个 cur 指针来扫描整个数组,另⼀个 dest 指针⽤来记录⾮零数序列的最后⼀个位置。根据 cur 在扫描的过程中,遇到的不同情况,分类处理,实现数组的划分。在 cur 遍历期间,使 [0, dest] 的元素全部都是⾮零元素, [dest + 1, cur - 1] 的元素全是零。

1.2 算法流程

  1. 初始化 cur = 0 (⽤来遍历数组), dest = -1 (指向⾮零元素序列的最后⼀个位置。因为刚开始我们不知道最后⼀个⾮零元素在什么位置,因此初始化为 -1 )
  2. cur 依次往后遍历每个元素,遍历到的元素会有下⾯两种情况:
    ①遇到的元素是 0 , cur 直接 ++ 。因为我们的⽬标是让 [dest + 1, cur - 1] 内的元素全都是零,因此当 cur 遇到 0 的时候,直接 ++ ,就可以让 0 在 cur - 1的位置上,从⽽在 [dest + 1, cur - 1] 内;
    ② 遇到的元素不是 0 , dest++ ,并且交换 cur 位置和 dest 位置的元素,之后让cur++ ,扫描下⼀个元素。
    。因为 dest 指向的位置是⾮零元素区间的最后⼀个位置,如果扫描到⼀个新的⾮零元素,那么它的位置应该在 dest + 1 的位置上,因此 dest 先⾃增 1 ;
    。dest++ 之后,指向的元素就是 0 元素(因为⾮零元素区间末尾的后⼀个元素就是0 ),因此可以交换到 cur 所处的位置上,实现 [0, dest] 的元素全部都是⾮零元素, [dest + 1, cur - 1] 的元素全是零。

1.3 代码实现

class Solution
{
public:
	 void moveZeroes(vector<int>& nums) 
	 {
		 for(int cur = 0, dest = -1; cur < nums.size(); cur++)
		 if(nums[cur]) // 处理⾮零元素
			 swap(nums[++dest], nums[cur]);
	 }
};

二、1089. 复写零

在这里插入图片描述

2.1 算法思路

如果「从前向后」进⾏原地复写操作的话,由于 0 的出现会复写两次,导致没有复写的数「被覆盖掉」。因此我们选择「从后往前」的复写策略。
但是「从后向前」复写的时候,我们需要找到「最后⼀个复写的数」,因此我们的⼤体流程分两
步:

  1. 先找到最后⼀个复写的数;
  2. 然后从后向前进⾏复写操作

2.2 算法流程

  • ①初始化两个指针 cur = 0 , dest = -1 ;
  • ② 找到最后⼀个复写的数:
    • 当 cur < n 的时候,⼀直执⾏下⾯循环:
      • 如果是 0 的话, dest 往后移动两位;否则, dest 往后移动⼀位。
    • 判断 dest 时候已经到结束位置,如果结束就终⽌循环;
    • 如果没有结束, cur++ ,继续判断。
  • ③判断 dest 是否越界到 n 的位置:
    • 如果越界,执⾏下⾯三步:
        1. n - 1 位置的值修改成 0 ;2. cur 向移动⼀步;3. dest 向前移动两步。
  • ④从 cur 位置开始往前遍历原数组,依次还原出复写后的结果数组:
    • 判断 cur 位置的值:
        1. 如果是 0 : dest 以及 dest - 1 位置修改成 0 , dest -= 2 ;
        1. 如果⾮零: dest 位置修改成 0 , dest -= 1 ;
    • cur-- ,复写下⼀个位置。

2.3 代码实现

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur=0, dest=-1, n=arr.size();
        //1. 先找到最后一个数
        while(cur<n)
        {
            if(arr[cur]) 
                dest++;
            else 
                dest+=2;
            if(dest+1>=n)
                break;
            cur++;
        }

        //2. 边界处理(dest可能超出范围)
        if(dest==n)
        {
            arr[n-1]=0;
            cur--, dest-=2;
        }

        //从后向前完成复学
        while(cur>=0)
        {
            if(arr[cur])
                arr[dest--]=arr[cur--];
            else
            {
                arr[dest--]=0;
                arr[dest--]=0;
                cur--;
            }
        }
    }
};

三、202. 快乐数

在这里插入图片描述

3.1 算法思路(快慢指针)

根据上述的题⽬我们可以知道,当重复执⾏ x 的时候,数据会陷⼊到⼀个「循环」之中。
⽽「快慢指针」有⼀个特性,就是在⼀个圆圈中,快指针总是会追上慢指针的,也就是说他们总会
相遇在⼀个位置上。如果相遇位置的值是 1 ,那么这个数⼀定是快乐数;如果相遇位置不是 1
的话,那么就不是快乐数。

3.2 代码实现

class Solution {
public:
    //用于求每个位置的平方和
    int BitSum(int x)
    {
        int sum=0;
        while(x)
        {
            int t = x % 10;//余数
            sum += t * t;
            x /= 10;
        }
        return sum;
    }

    //判断是否为快乐数
    bool isHappy(int n)
    {
        int slow=n, fast=BitSum(n);
        while(slow != fast)
        {
            slow=BitSum(slow);
            fast=BitSum(BitSum(fast));
        }
        //相遇,判断
        return slow == 1;
    }
};

四、11. 盛最多水的容器

在这里插入图片描述

4.1 算法思路(对撞指针)

设两个指针 left , right 分别指向容器的左右两个端点,此时容器的容积 :
v = (right - left) * min( height[right], height[left])
容器的左边界为 height[left] ,右边界为 height[right] 。


为了⽅便叙述,我们假设「左边边界」⼩于「右边边界」。

  • 如果此时我们固定⼀个边界,改变另⼀个边界,⽔的容积会有如下变化形式:
    • 容器的宽度⼀定变⼩。
    • 由于左边界较⼩,决定了⽔的⾼度。如果改变左边界,新的⽔⾯⾼度不确定,但是⼀定不会超过右边的柱⼦⾼度,因此容器的容积可能会增⼤。
    • 如果改变右边界,⽆论右边界移动到哪⾥,新的⽔⾯的⾼度⼀定不会超过左边界,也就是不会超过现在的⽔⾯⾼度,但是由于容器的宽度减⼩,因此容器的容积⼀定会变⼩的。

由此可⻅,左边界和其余边界的组合情况都可以舍去。所以我们可以 left++ 跳过这个边界,继续去判断下⼀个左右边界。当我们不断重复上述过程,每次都可以舍去⼤量不必要的枚举过程,直到 left 与 right 相遇。期间产⽣的所有的容积⾥⾯的最⼤值,就是最终答案。

代码实现

class Solution {
public:
    int maxArea(vector<int>& height) {
        int ret=0;
        int left=0, right=height.size()-1;
        while(left < right)
        {
            int capacity = min(height[left], height[right])*(right - left);
            ret =max(ret, capacity);
            if(height[left] < height[right])
                left++;
            else
                right--;
        }
        return ret;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/234967.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

neuq-acm预备队训练week 8 P8794 [蓝桥杯 2022 国 A] 环境治理

题目描述 输入格式 输出格式 输出一行包含一个整数表示答案。 输入输出样例 解题思路 最短路二分 AC代码 #include<bits/stdc.h> using namespace std; long long temp,n, Q; long long f[105][105],min_f[105][105],cut[105],dis[105][105];//cut为减少多少&#x…

在 Qt Creator 中编写 Doxygen 风格的注释

2023年12月10日&#xff0c;周日上午 如何生成Doxygen 风格的注释 在需要Doxygen 风格注释的函数上方输入 /**&#xff0c;然后按下 Enter 键。Qt Creator 将自动为你生成一个注释模板。 输入&#xff0c;Qt Creator会自动帮你补全Doxygen标签 不得不说&#xff0c;写了Doxyge…

【HarmonyOS开发】详解常见容器的使用

声明式UI提供了以下8种常见布局&#xff0c;开发者可根据实际应用场景选择合适的布局进行页面开发。 布局 应用场景 线性布局&#xff08;Row、Column&#xff09; 如果布局内子元素超过1个&#xff0c;且能够以某种方式线性排列时优先考虑此布局。 层叠布局&#xff08;St…

使用alpine镜像部署go应用时踩的坑

使用alpine镜像部署go应用时踩的坑 关于交叉编译 实际上我在ubuntu的交叉编译出来的exe并不能在alpine上运行&#xff0c;这边采取拉镜像编译复制出来的做法&#xff0c;部署再用干净的alpine 拉取golang:alpine踩坑 在Dockerhub上可以找到&#xff1a; 然而拉取的alpine中…

Cpolar配置外网访问和Dashy

Dashy是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航页。一款功能超强大,颜值爆表的可定制专属导航页工具 结合cpolar内网工具,我们实现无需部署到公网服务器…

周周清(2)----踩坑日记

周一&#xff1a; 1.之前换了一个jdk&#xff0c;然后又改了很多东西&#xff0c;很乱&#xff0c;以至于很多项目都不能直接运行了&#xff0c;所以今天就将ideal删除并且更新版本到2022.3.3&#xff0c;并且重新将ideal里面的配置环境变量&#xff0c;以及jdk下载安装配置&a…

【论文阅读笔记】NeRF+Mip-NeRF+Instant-NGP

目录 前言NeRF神经辐射场体渲染连续体渲染体渲染离散化 方法位置编码分层采样体渲染推导公式&#xff08;1&#xff09;到公式&#xff08;2&#xff09;部分代码解读相机变换&#xff08;重要&#xff01;&#xff09; Mip-NerfTo do Instant-NGPTo do 前言 NeRF是NeRF系列的…

【unity小技巧】实现枪武器随镜头手臂摇摆效果

文章目录 前言方法一、改变武器位置方法二、改变武器旋转结语完结 前言 如果我们视角移动转向&#xff0c;武器如果不跟着进行摇摆&#xff0c;会感觉我们的动作很生硬&#xff0c;特别是射击类游戏&#xff0c;如下 实现武器摇摆这里主要分享两种实现方法&#xff0c;一种是…

git学习笔记03(小滴课堂)

详解分支的基本操作 创建分支&#xff1a; 查看分支&#xff1a; 切换分支&#xff1a; git branch 中星号是当前分支。 idea中也更新了。 提交上去。 我们新建个分支&#xff1a; 我们新建分支是复制当前分支&#xff0c;而不是直接复制的主分支。 我们切换回主分支&#xf…

Docker 入门

Docker 入门 基础 不同操作系统下其安装包、运行环境是都不相同的&#xff01;如果是手动安装&#xff0c;必须手动解决安装包不同、环境不同的、配置不同的问题 而使用Docker&#xff0c;这些完全不用考虑。就是因为Docker会自动搜索并下载MySQL。注意&#xff1a;这里下载…

TeeChart.NET 2023.11.17 Crack

.NET 的 TeeChart 图表控件提供了一个出色的通用组件套件&#xff0c;可满足无数的图表需求&#xff0c;也针对重要的垂直领域&#xff0c;例如金融、科学和统计领域。 数据可视化 数十种完全可定制的交互式图表类型、地图和仪表指示器&#xff0c;以及完整的功能集&#xff0c…

【Spring Boot 源码学习】ApplicationListener 详解

Spring Boot 源码学习系列 ApplicationListener 详解 引言往期内容主要内容1. 初识 ApplicationListener2. 加载 ApplicationListener3. 响应应用程序事件 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们从 Spring Boot 的启动类 SpringApplication 上入手&am…

使用WebyogSQLyog使用数据库

数据库 实现数据持久化到本地&#xff1a; 使用完整的管理系统统一管理&#xff0c; 数据库&#xff08;DateBase&#xff09;&#xff1a; 为了方便数据存储和管理&#xff08;增删改查&#xff09;&#xff0c;将数据按照特定的规则存储起来 安装WebyogSQLyog -- 创建数…

Elasticsearch:向量数据库的真相

通过工作示例了解什么是向量数据库、它们如何实现 “相似性” 搜索以及它们可以在明显的 LLM 空间之外的哪些地方使用。除非你一直生活在岩石下&#xff0c;否则你可能听说过诸如生成式人工智能和大型语言模型&#xff08;LLM&#xff09;之类的术语。 除此之外&#xff0c;你很…

nodejs+vue+微信小程序+python+PHP个性化服装搭配系统APP-计算机毕业设计推荐 android

考虑到实际生活中在个性化服装搭配方面的需要以及对该系统认真的分析,将app权限按管理员和用户这两类涉及用户划分。 (a) 管理员&#xff1b;管理员使用本系统涉到的功能主要有个人中心、用户管理、个性穿搭管理、我的衣橱管理、服饰分类管理、我的收藏管理、系统管理等功能。 …

大华摄像头windows、linuxJavaSDK开发使用

文章目录 简介环境要求库加载问题及解决方法大华摄像头Java SDK&#xff0c;完成摄像头设备登录、视频录像目录结构windows 的c代码Linux的C代码项目结构 登录云台控制录像调用的接口注意码云地址 简介 本文档主要介绍 SDK 接口参考信息&#xff0c;包括主要功能、接口函数和回…

小模型学习(1)-人脸识别

【写作背景】因为最近一直在研究大模型&#xff0c;在与客户进行交流时&#xff0c;如果要将大模型的变革性能力讲清楚&#xff0c;就一定要能将AI小模型的一些原理和效果讲清楚&#xff0c;进而形成对比。当然这不是一件简单的事情&#xff0c;一方面大模型分析问题的的本质原…

Flask和Vue框架实现WebSocket消息通信

1 安装环境 1.1 安装Flask环境 主要的安装包 Flask、Flask-SocketIO&#xff0c;注意Python版本要求3.6 # Flask-SocketIO参考地址 https://flask-socketio.readthedocs.io/en/latest/ https://github.com/miguelgrinberg/flask-socketio更新基础环境 # 更新pip python -m …

JVM垃圾回收

文章目录 垃圾回收四种引用引用计数算法可达性分析算法 垃圾回收算法标记清除标记整理复制 分代回收GCGC相关参数GC分析大对象 垃圾回收器串行吞吐量优先响应时间优先 垃圾回收 四种引用 强引用 new创建一个对象&#xff0c;通过等号运算符赋值给一个变量&#xff0c;那么这个…

Vue3中的defineModel

目录 一、vue3的defineModel介绍 二、defineModel使用 &#xff08;1&#xff09;在vite.config.js中开启 &#xff08;2&#xff09;子组件 &#xff08;3&#xff09;父组件 一、vue3的defineModel介绍 为什么要使用到defineModel呢&#xff1f;这里有这样一种场景&…