双指针算法(一)

目录

移动零

复写零

快乐数

盛水最多的容器

双指针与单调性结合

有效三角形的个数

查找总价格为目标值的两个商品

两数之和 Ⅱ - 输入有序数组 


双指针算法是通过定义两个指针不断单向移动来解决问题的一种算法。但双指针算法,是一个抽象的思想概念,可以用来解决数组划分、数组分块等问题。

它通常并不是真的定义两个指针,例如在 vector、string 中就经常通过下标来充当指针。

移动零

力扣链接:移动零

题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

题目的意思:很明确,将所有非零元素移至数组最前面,数组中的零都放在数组偏后面的位置。

思路:定义两个 ‘指针’ cur 和 dest ,cur指针用来从左往右扫描遍历数组,一直指向扫描过的最后一个元素,dest指针一直指向遍历过的数组范围内最后一个不为零的位置。

具体做法:定义cur为0,dest为 -1(因为不知道第一个位置是否需要被交换),cur指针从左往右扫描数组,遇到等于0的元素直接跳过,遇到不等于零的元素,先让 dest 指针的位置加1,然后交换 cur 和 dest 两个位置的值后,两个指针再分别后移一位......如此循环直到最后cur扫描至数组最后一位。

此时数组就被划分为两个部分,因为dest指向的是遍历过的数组范围内最后一个不为零的位置,而扫描已经结束,所以 dest 指针指向的是最后一个不为 0 的位置,cur指针指向的是数组最后一个位置,两个指针中间的数就全是移动后的零。

代码详解(核心代码模式)

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int cur=0,dest=-1;
        while(cur<nums.size())
        {
            if(nums[cur]==0)
                cur++;
            else
            {
                dest++;
                swap(nums[dest],nums[cur]);
                cur++;
            }
        }
    }
};

复写零

力扣链接:复写零

题目:给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

思路:先根据 ‘异地’ 操作将数组进行按规则复写,然后优化为双指针下的 ‘就地’ 操作。

异地操作方法:两个指针 cur 和 dest 分别指向两个数组,一个原数组,一个与原数组空间相同的新数组。利用 cur 指针对原数组进行遍历,遇到非零元素,在新数组中按序拷贝,遇见0元素,就在新数组中连续拷贝两次。 

原地操作优化思路:先使用 ‘异地’ 操作的思想,找到最终两个指针复写结果的位置,从前往后完成复写!

具体做法:

(1)定义 cur 为0,dest 为 -1。cur 指针遍历数组,遇到不为0的数,dest向前移动一位,遇到0,dest 向前移动两位,当 dest 到达数组最后一个位置的时候,cur和dest的位置就是从后往前开始复写的位置。

(2)cur 指针开始向前走,遇到非零,dest就将这个数拷贝过去,dest前移一位;遇到0,dest就拷贝两个零,同时向前移动两位.....当cur移动到数组最开始的位置的时候,复写就结束了!

代码详解(核心代码模式)

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur = 0, dest = -1;
        int n = arr.size();
        //第一步:找到复写的位置
        while (dest <n)
        {
            if (arr[cur] != 0)
                dest++;
            else if (arr[cur] == 0)
                dest += 2;
            if (dest >=n - 1)
                break;
            cur++;
        }
        //处理特殊情况
        if (dest==n)//dest指针越界,说明原数组倒数第二个位置为0
        {
            cur--;
            arr[n - 1] = 0;
            dest -= 2;
        }
        while (cur >= 0)
        {
            if (arr[cur] != 0)
            {
                arr[dest--] = arr[cur];
            }
            else if (arr[cur] == 0)
            {
                arr[dest--] = arr[cur];
                arr[dest--] = arr[cur];
            }
            cur--;
        }
    }
};

注意点:当原数组的倒数第二个位置为0的时候,需要在循环结束后单独处理一下,否则就会造成数组越界! 

快乐数

力扣链接:快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

思路:快乐数定义的第二点非常重要,‘重复这个过程直到这个数字变为 1 ,也可能是 无限循环 但始终变不到1’。那么这个问题中,运算每进行一步,都可以看做链表往前走一步,其实可以抽象成链表成环问题,再使用 ‘快慢指针’ 来解决。

这里的快指针就是计算每位平方和后再将结果计算平方和,慢指针就只计算一次平方和......一直进行这样的操作,既然这个数字最后都会‘循环’,那么就是变成1循环还是其他多个数进行循环的问题了。

当快指针和慢指针相遇(相等)的时候,判断这个数是不是1,如果是1,这个数 n 就是快乐数,反之则不是。

代码详解(核心代码模式)

class Solution {
public:
    int Sumbit(int n)
    {
        int sum = 0;
        while(n)
        {
            int x = n % 10;
            sum += x*x;
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        //快慢指针
        int fast = n,slow = n;
        do
        {
            slow = Sumbit(slow);
            fast = Sumbit(Sumbit(fast));    
        }while(fast != slow);
        if(fast == 1)
            return true;
        else
            return false;
    }
};

快指针和慢指针刚开始都等于这个数字 n,定义一个计算各位平方和的函数:慢指针调用1次,快指针调两次。然后使用 do...while 循环找到下次 快慢指针相等的值,判断这个值是否等于1即可。

盛水最多的容器

力扣链接:盛水最多的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

思路:容器能盛水的最大高度是由两侧最低的柱子决定的!先计算出两侧开始的水的容量,设置为Max。利用双指针从两边到中间,高度较低一边的指针向中间移动,并重新计算容量,如果新容量大于初始的Max,就更新Max的值。两个指针相遇的时候,Max的值就是容器能容纳水的最大值。

代码详解(核心代码模式)

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

双指针与单调性结合

有效三角形的个数

力扣链接:有效三角形的个数

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

前提:一组数中,两个较小的数相加大于较大的数,就能组成三角形(任意三边之和大于第三边的一组数能构成三角形的推论)

思路:先排序,保证不降序排列。从数组右侧开始固定最大的数,利用双指针(一个指针在最左边,另一个指针在固定数的左边),来确定固定的这个数的左半区间中能构成三角形的个数,然后向左更新这个被固定的数,重复上面的步骤,直至左半区间只有两个数为止循环结束。

确定左半区间中能构成三角形的个数:设固定的数位置为 tmp,则right的位置为 tmp -1 ,left 位置的值为 0。判断两个位置数相加后的结果与固定数的大小如果大于,说明两个指针 [ left 、right ) 之间的所有数,都能与 right、tmp形成三角形,则能形成三角形的个数将会增加 ( right - left )个如果小于等于,说明 left 这边的值不够大,将 left 指针再加1,重复上面的比较过程。当left指针和right指针相遇,或者两者的数大于 tmp 位置的数时结束本轮循环,更新固定数的位置继续上述过程。

代码详解(核心代码模式)

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        //只要两个小数相加大于大数,就能构成三角形
        sort(nums.begin(),nums.end());//先排序
        size_t sum = 0;
        //外层循环,固定一个最大的数
        for(int tmp = nums.size()-1; tmp >= 2; tmp --)
        {
            int right = tmp-1;
            int left = 0;
            //利用双指针算法
            while(right != left)
            {
                if(nums[left] + nums[right] > nums[tmp])
                {
                    sum += (right-left);
                    right--;
                }
                else
                {
                    left++;
                }
            }
        }
        return sum;
    }
};

查找总价格为目标值的两个商品

两数之和 Ⅱ - 输入有序数组 

这两个题是一种思路,一个返回数。一个返回下边。但都是双指针思路、利用单调性来做题!

设定两个指针,一个在左边,一个在右边,因为数组是有序的,所以左边是最小的,右边是最大的,两个指针往中间移动,根据和与目标数值的大小关系,来确定指针的移动,最后两个指针停留于的位置,就是两个数的和等于目标值的位置。 

查找总价格为目标值的两个商品代码详解(核心代码模式)

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

两数之和 Ⅱ - 输入有序数组(核心代码模式) 

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

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

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

相关文章

【C语言】超详解strncpystrncatstrncmpstrerrorperror的使⽤和模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

智能优化算法应用:基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.原子搜索算法4.实验参数设定5.算法结果6.…

12.4~12.14概率论复习与相应理解(学习、复习、备考概率论,这一篇就够了)

未分配的题目 概率计算&#xff08;一些转换公式与全概率公式&#xff09;与实际概率 &#xff0c;贝叶斯 一些转换公式 相关性质计算 常规&#xff0c;公式的COV与P 复习相关公式 计算出新表达式的均值&#xff0c;方差&#xff0c;再套正态分布的公式 COV的运算性质 如…

FastAPI访问/docs接口文档显示空白、js/css无法加载

如图&#xff1a; 原因是FastAPI的接口文档默认使用https://cdn.jsdelivr.net/npm/swagger-ui-dist5.9.0/swagger-ui.css 和https://cdn.jsdelivr.net/npm/swagger-ui-dist5.9.0/swagger-ui-bundle.js 来渲染页面&#xff0c;而这两个URL是外网的CDN&#xff0c;在国内响应超…

五、Java核心数组篇

1.数组 概念&#xff1a; ​ 指的是一种容器&#xff0c;可以同来存储同种数据类型的多个值。 ​ 但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 比如&#xff1a; ​ 定义了一个int类型的数组。那么boolean。double类型的数据是不能存到这个数组中的&…

【RocketMQ】顺序消费消息实现原理分析

一、顺序消息概述 1.1、什么是顺序消息 顺序消息是指对于一个指定的 Topic &#xff0c;消息严格按照先进先出&#xff08;FIFO&#xff09;的原则进行消息发布和消费&#xff0c;即先发布的消息先消费&#xff0c;后发布的消息后消费。 1.2、顺序消息的类型 全局顺序消息 …

经典深度学习算法【1】:K-近邻算法(KNN)概述

最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来&#xff0c;当测试对象的属性和某个训练对象的属性完全匹配时&#xff0c;便可以对其进行分类。但是怎么可能所有测试对象都会找到与之完全匹配的训练对象呢&#xff0c;其次就是存在一个测试对象同时与多个训练…

JVM虚拟机系统性学习-JVM调优之GC日志分析

JVM 调优 首先&#xff0c;为什么要 JVM 调优呢&#xff1f; JVM 调优的目的就是为了让应用程序使用最小的硬件消耗来承载更大的吞吐量 什么情况下需要 JVM 调优呢&#xff1f; 系统吞吐量下降&#xff0c;或系统延迟较高出现 OOMFull GC 频繁GC 停顿时间过长&#xff08;超…

UE虚幻引擎中程序无需运行也可调试

首先先新建一个蓝图类&#xff0c;在蓝图类中创建一个Custom event 事件&#xff0c;然后在右侧细节面板中搜索call in editor&#xff0c;编译保存之后&#xff0c;将该蓝图类拖拽到关卡场景中&#xff0c;在细节面板中即可看到该事件的按钮。

PE文件格式-PE文件头部

文章目录 PE文件头基本概念简介地址对齐 PE结构概述PE文件头部DOS MZ 头PE头&#xff08;NT头&#xff09;PE头标识 Signature标准PE头 IMAGE_FILE_HEADERMachineNumberOfSectionsTimeDateStampPointToSymbolTableNumberOfSymbolSizeOfOptionalHeaderCharacteristics 扩展PE头 …

亚信科技AntDB数据库——深入了解AntDB-M元数据锁的相关概念

AntDB-M在架构上分为两层&#xff0c;服务层和存储引擎层。元数据的并发管理集中在服务层&#xff0c;数据的存储访问在存储引擎层。为了保证DDL操作与DML操作之间的一致性&#xff0c;引入了元数据锁&#xff08;MDL&#xff09;。 AntDB-M提供了丰富的元数据锁功能&#xff…

【Hadoop】执行start-dfs.sh启动hadoop集群时,datenode没有启动怎么办

执行start-dfs.sh后&#xff0c;datenode没有启动&#xff0c;很大一部分原因是因为在第一次格式化dfs后又重新执行了格式化命令&#xff08;hdfs namenode -format)&#xff0c;这时主节点namenode的clusterID会重新生成&#xff0c;而从节点datanode的clusterID 保持不变。 在…

认识缓存,一文读懂Cookie,Session缓存机制。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十四:系统设置模块相关功能实现

一、本章内容 本章使用已实现的公共组件实现系统管理中的系统设置模块相关功能,包括菜单管理、角色管理、日志管理、用户管理、系统配置、数据字典等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址:

硬件基础常识【4】--利用戴维宁定理求运放复杂反馈电阻网络的增益

最近学到了一种求带T型电阻网络反馈运放增益的方法 如图所示为T型电阻网络反馈的反相放大器 求解思路 沿X-Y断开&#xff0c;右侧利用戴维宁定理等效成电压源串电阻的形式 由戴维宁定理可得&#xff1a; V T H V o u t ∗ R 4 / ( R 3 R 4 ) ( 式 1 ) VTHVout*R4/(R3R4)…

布局前沿技术,紫光展锐推动6G创新融合发展

随着5G进入规模化商用阶段&#xff0c;6G研究已在全球范围内拉开帷幕。2023年6月&#xff0c;ITU发布了《IMT面向2030及未来发展的框架和总体目标建议书》&#xff0c;在升级5G三大应用场景的同时&#xff0c;扩展出三个跨领域场景&#xff0c;形成6G的六大应用场景&#xff0c…

unity—UGUI 点击按钮后,持续点击空格键会持续出发按钮

在unity开发当中&#xff0c;使用UGUI开发&#xff0c;无论是你代码绑定按钮事件&#xff0c;还是在Inspector窗口直接拖拽绑定的事件&#xff0c;点击按钮事件后&#xff0c;按空格键都会再次执行相关的方法。 默认情况下&#xff0c;Unity将空格键映射为UI按钮的Submit提交操…

【Hadoop】

Hadoop是一个开源的分布式离线数据处理框架&#xff0c;底层是用Java语言编写的&#xff0c;包含了HDFS、MapReduce、Yarn三大部分。 组件配置文件启动进程备注Hadoop HDFS需修改需启动 NameNode(NN)作为主节点 DataNode(DN)作为从节点 SecondaryNameNode(SNN)主节点辅助分…

Ubuntu18.04.6下samba服务的安装及配置

目录 01 安装samba服务&#xff1a; 03 重启samba服务 04 设置samba登录密码 05 测试 前言 虚拟机下Ubuntu18.04.6samba服务的安装及配置 01 安装samba服务&#xff1a; 命令行中输入 sudo apt-get install samba 02 配置 2.1 先创建一个需要共享的目录&#xff0c…

利用高级 CSPM 应对现代攻击

混合云和多云环境的快速增长造成了跨架构的复杂性&#xff0c;使得人们很难清楚、完整地了解技术堆栈中的各种平台。最近云攻击和破坏的激增引起了人们对团队应如何有效地保护和运行云中的应用程序的关注。 因为错误配置是云环境中安全威胁的首位&#xff0c;并且是基于云的攻…