【算法专题】双指针算法

1. 移动零

题目分析

对于这类数组分块的问题,我们应该首先想到用双指针的思路来进行处理,因为数组可以通过下标进行访问,所以说我们不用真的定义指针,用下标即可。比如本题就要求将数组划分为零区域和非零区域,我们不妨定义两个指针cur和dest,cur不断向后遍历,dest则指向已处理区间内最后一个非零元素,当cur找到一个非零元素时,就把dest++并交换dest和cur对应的数组元素

那么在处理数组的过程中,数组被划分为了三块区域:

[0, dest]  [dest+1, cur] [cur+1, n-1]

分别代表:非零区域、零区域、未处理区域

当处理结束后只剩下非零区域和零区域了,而我们也实现了题目的要求,把数组中的所有元素都移动到数组末尾,且不改变其他元素的次序

实现代码:

class Solution {
public:void moveZeroes(vector<int>& nums) {int dest = -1, cur = 0; // 我们一开始并不知道dest的位置,暂时定义为-1while(cur < nums.size()) {if(nums[cur] == 0) {cur++;}else if(nums[cur] != 0) {dest++;swap(nums[dest], nums[cur]);cur++;}}}
};

2. 复写零

 1089. 复写零 - 力扣(LeetCode)

题目描述:

依据题意,数组中的零是会被重复写一遍的,那么一旦数组中有0,数组后面肯定就会有元素被覆盖掉,所以我们肯定是不能从前向后进行处理的。

为了保证不漏掉元素,我们应该要找到最后一个没有被覆盖的元素,然后把这个元素填到数组最后面,接着向前遍历,如果碰到的元素是非零,就填一次,如果是零就填两次,这样就实现了把所有的零都复写一遍,显然这个过程我们还是需要两个指针来帮助我们完成覆盖的操作。

所以我们接下来要实现的就是:

1. 找到最后一个“复写”的数

两个指针cur和dest,cur向后遍历,当碰到0时,dest向后移动两位,否则向后移动一位,这样,当dest指向数组末尾时,cur指向的位置就是数组中最后一个需要复写的元素位置了

2. 从前向后进行复写

在第一步处理之后,dest指向了数组末尾,cur指向了最后一个要复写的下标,cur和dest开始从后往前移动,并把cur元素覆盖到dest位置上,如果cur元素为0,就覆盖两次

3. 边界情况

一般情况下,上述的第一步处理中,dest是能正常移动到n-1位置的,但是存在这样一种情况:

当数组内容如上所示时,第一步的操作就会出现问题,dest指向了数组长度之外的位置,我们需要对这种情况进行特殊处理

实现代码:

class Solution {
public:void duplicateZeros(vector<int>& arr) {int cur = 0, dest = -1;int n = arr.size();while(cur <= n - 1) // 找到最后一个复写元素{if(arr[cur]) dest++;else dest += 2;if(dest >= n - 1){break;}cur++;}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--;}}}
};

3. 快乐数

202. 快乐数 - 力扣(LeetCode)

题目分析

        依题意,对于一个正整数,如果每次将其替换为每个位置上数字的平方和,那么这个数最终可能为1或进入无限循环。事实上,这一点我们是可以证明的,首先给大家讲一下鸽巢原理:如果有n个巢穴,n+1只鸽子,那么至少有一个巢穴是有两只鸽子的。然后接下来解决本题,n的范围如上,如果将其最大值替换为每个位置上数字的平方和,2^31-1 的值为2,147,483,647,就算把这十位数换成9,999,999,999,按照这个算法,结果也只有810。

        也就是说,本题n取值范围内的所有数,经过处理得到的结果都不超过810,所以只要n经过811次变换,最后一定至少出现一次重复的元素,也就进入了循环。原因是:前810次n已经将所有变换的可能结果枚举了一遍,第811次的结果必定在前810次变换结果的集合之中,所以必定重复。

算法原理

        经过上述证明,我们清楚了n经过足够多次数的变换,只可能进入重复循环或变为1,而1的平方和恒为1,也算一个循环。所以我们要判断一个数是不是快乐数,只需要判断在循环中,这个数是不是1即可。

        这就要用到快慢指针算法了,fast指针一次移动两个单位,slow指针一次移动一个单位,则一旦fast和slow相遇,就说明已经在循环中了,此时仅需判断相遇时值是否为1即可。

代码如下:

class Solution {
public:// 进行变换int getsum(int n) {int sum = 0;while(n) {int tmp = n % 10;sum += tmp * tmp;n /= 10;}return sum;}bool isHappy(int n) {int fast = n, slow = n;do {// fast每次移动两个单位,slow移动一个单位fast = getsum(getsum(fast));slow = getsum(slow);} while(fast != slow);// 最后仅需判断相遇时值是否为1return fast == 1;}
};

4. 盛水最多的容器 

​​​​​​11. 盛最多水的容器 - 力扣(LeetCode)

题目解析

        根据题目示例,我们发现容器高度是由两端的柱子中较短的一根所决定的,设其高度为h,两根柱子的距离设为l,则我们要找的就是h*l最大的组合

算法原理

解法一 暴力解法

        从起始位置开始,将所有的位置都枚举一遍,最后肯定能找到盛水最多的容器,时间复杂度是O(n^2)但这样做肯定是会超时的,我们必须想一种更优秀的方法

解法二 双指针算法

        我们先用一个小区间来举例子,在题目解析中,我们看出了,容器高度由两端中较短的一方决定。在这个例子中,如果我们固定较短的柱子,移动另一端,可以发现,容器的容积是在减小的,问题在于:

1. 向内枚举,区间长度是一定减小的

2. 固定了短端,移动另一端,如果另一端比短端短,高度减小;就算比短端长,高度也不会变大

        所以趋势是:l一定减小,h不变或减小,v = h * l,则容积也必定减小!

        而如果我们固定较长的柱子,移动另一端,情况则有不同,如果短端找到更长的柱子,容器的容积是有可能增大的

        所以我们可以定义两个指针left、right在区间的0和n-1位置,找到短的一方,计算出当前容积,移动长的一方,这样我们只需要遍历数组一遍就能够找到最大值,时间复杂度为O(n)

class Solution {
public:int maxArea(vector<int>& height) {int l = 0, r = height.size() - 1;int h, w;int v = 0;while(l != r) {h = height[l] < height[r] ? height[l] : height[r];w = r - l;int tmp = h * w;v = tmp > v ? tmp : v;if(height[l] < height[r]) l++;else r--;}return v;}
};

5. 有效三角形的个数

 611. 有效三角形的个数 - 力扣(LeetCode)

题意分析:

        我们都知道,三角形的三条边满足任意两条边之和大于第三条边,但是还有一个更进一步的结论:假设a < b < c,则只要三个数满足a + b > c,就能保证任意两边之和大于第三条边。

        因此本题就转化为了,找到数组中满足 a < b < c 且 a + b > c 的三元组个数。

算法原理:

解法一:暴力解法

        用三层for循环列举出所有的三元组,然后判断是否满足构成三角形的条件,则时间复杂度:3*n^3,进行一下优化,如果我们先把数组排序,那么判断是否满足条件就只需要一次,时间复杂度为:n*logn + n^3,显然时间复杂度还是O(n^3),肯定是会超时的,我们需要想办法减小时间复杂度。

解法二:双指针算法

        在前面我们分析过了,要找的是满足 a < b < c 且 a + b > c 的三元组个数,和解法一中的优化一样,我们先将数组进行排序,则c肯定就在数组的末端找了,所以我们不妨先固定c,再找符合条件的a、b即可。

        可是如果我们还是用遍历的方式去找a和b,那时间复杂度根本就没有降低,还是O(n^3),所以必须换一个思路,既然 a < b ,我们不妨定义两个指针:left,right,分别从数组左右端移动,与上一道题类似的,我们要通过单调性来决定是移动left还是right!

        如果 a + b > c,因为数组排过序,是单调递增的,left++过程中是始终满足 a + b > c 的,由于本题求的是三元组个数,没必要进行left++了,直接right - left求个数即可。之后我们再进行right--,当left  == right时,说明这个c为最长边的情况列举完了,接着求下一个c为最长边的情况。

        如果 a + b <= c,因为数组排过序,是单调递增的,如果我们让right--,a + b 只能更小,因此只能让left++,直到 a + b > c 或 left == right。

代码如下:

class Solution {
public:int triangleNumber(vector<int>& nums) {int a, b, c;int max = nums.size() - 1;int cnt = 0;sort(nums.begin(), nums.end());while(max > 1){int left = 0, right = max - 1;while(left != right){a = nums[left], b = nums[right], c = nums[max];if(a + b > c){cnt += right - left;right--;}else left++;}max--;}return cnt;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

时序分析基本概念介绍——SI/crosstalk/delta delay/noise/timing Window

文章目录 前言一、Crosstalk1. Crosstalk Delay Effects2. Crosstalk Noise Effects 二、Crosstalk Analysis1. Crosstalk Delay Analysis2. Crosstalk Noise Analysis 三、如何 fix delta delay 和 noise violations1. 检查delta delay 和 noisedelta delay checknoise check …

【C语言小知识】缓冲区

缓冲区 当我们使用printf()将数据显示在屏幕上&#xff0c;或者使用scanf()函数将数据输入到电脑里&#xff0c;我们是否会产生些许疑问&#xff1f;为何输入的字符会直接显示到屏幕上等等。这里需要介绍一个C语言中的一个关键概念——缓冲区。 当我们使用老式系统进行运行代码…

suricata7 rule加载(一)加载 action

suricata7.0.5 一、前提条件 1.1 关键字注册 main | --> SuricataMain|--> PostConfLoadedSetup|--> SigTableSetupsigmatch_table是一个全局数组&#xff0c;每个元素就是一个关键字节点&#xff0c;是对关键字如何处理等相关回调函数。非常重要的一个结构&#x…

C语言牢大坠机

目录 开头程序程序的流程图《牢大坠机》&#xff08;视频正在审核中&#xff09;结尾 开头 大家好&#xff0c;我叫这是我58&#xff0c;今天&#xff0c;我们要来看关于牢大坠机的一些东西。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #define HIGH 66 #include <stdio.h…

ubuntu防火墙指定端口开放设置

文章目录 打开某个端口查看防火墙状态和开放端口例子 在 Ubuntu 中管理防火墙通常使用 ufw&#xff08;Uncomplicated Firewall&#xff09;。以下是打开某个端口并查看防火墙状态的方法&#xff1a; 打开某个端口 启用 ufw&#xff08;如果尚未启用&#xff09;&#xff1a; …

PyCharm如何安装requirements.txt中的依赖包

问题&#xff1a;下载别人的源码&#xff0c;如何安装代码中requirement.txt中的依赖包。 解决方案&#xff1a; &#xff08;1&#xff09;打开PyCharm下面的Terminal&#xff0c;先为代码创建单独的虚拟环境并进入到虚拟环境中&#xff08;每个项目单独的环境&#xff0c;这…

GlusterFS分布式存储系统

GlusterFS分布式存储系统 一&#xff0c;分布式文件系统理论基础 1.1 分布式文件系统出现 计算机通过文件系统管理&#xff0c;存储数据&#xff0c;而现在数据信息爆炸的时代中人们可以获取的数据成指数倍的增长&#xff0c;单纯通过增加硬盘个数来扩展计算机文件系统的存储…

ubuntu的screen会话,断开远程连接也能照样运行程序

文章目录 创建新的 screen 会话管理 screen 会话例子关闭某一个 screen 会话删除某一个 screen 会话 在 Ubuntu 中使用 screen 工具可以创建和管理多个终端会话。以下是创建 screen 会话的方法&#xff1a; 创建新的 screen 会话 启动一个新的 screen 会话&#xff1a; screen…

Docker搭建MySQL双主复制详细教程

在此之前需要提前安装好Docker和 Docker Compose 。 一、创建目录 首先创建一个本地数据挂载目录。 mkdir -p master1-data master2-data二、编写docker-compose.yml version: 3.7services:mysql-master1:image: mysql:5.7.36container_name: mysql-master1environment:MYSQL_…

VBA初学:零件成本统计之四(汇总计算)

第四步&#xff0c;最后进行汇总计算 汇总统计的计算 Sub count() Dim rng As Range Dim i As Long, j As Long Dim arr_s, arr, brr, crr, drr Dim rowscount As Long Dim X As Variant Dim rg As Single, xb As Single, zj As SingleMsgBox "汇总计算时间较久&#xff…

【HTML入门】第二课 - head标签下的常见标签们

目录 1 本节概要 2 head下的常见标签 2.1 网页编码设置 2.2 网页的标题 2.3 样式标签 3 head标签的内容不会显示到网页上 4 查看网页源代码 1 本节概要 上一节&#xff0c;我们说了HTML网页最基本的框架标签&#xff0c;说到标签分为head头部和body身体部分。这一小节呢…

sqlite3数据库常用的API接口

使用sqlite3的API接口 需要加头文件 #include 编译时&#xff0c;需要连接sqlite3的库 -lsqlite3 1.打开数据库文件的函数int sqlite3_open(const char *filename, /* Database filename (UTF-8) */sqlite3 **ppDb /* OUT: SQLite db handle */ );功能&#xff1…

Windows Server 2016 搭建 网络负载平衡 服务

网络负载平衡功能的安装 添加角色 默认不动————功能 勾选上 < 网络负载平衡 > 在工具中————打开 < 网络负载平衡管理器 > 网络负载平衡群集创建 注意 : 提前 将两台 web 站点服务器 都安装好 < 网络负载平衡功能 > 右键 选择 ————新建群集 ——…

5、RDB与AOF对比

RDB与AOF对比 1、RDB优势与不足 1.1、优势 RDB文件小 数据恢复快 1.2、不足 数据安全性较差写时复制技术会减低性能可读性较差 2、AOF优势与不足 2.1、优势 数据安全性高可读性高 2.2、不足 数据恢复较慢AOF文件较大写操作会影响性能 3、执行化技术的选型 官方推荐…

刷题——二叉搜索书的最近公共祖先

二叉搜索树的最近公共祖先_牛客题霸_牛客网 vector<int>getPath(TreeNode* root, int target) {TreeNode* node root;vector<int>path;while(node->val ! target){path.push_back(node->val);if(target < node->val)node node->left;elsenode n…

【学习笔记】爱立信SPO 1400 CRAFT软件基础知识6——配置的备份与恢复的详细方法

一、前期准备 提示&#xff1a;下面所有学习内容都是基于以下条件完成的 条件1.已经正确安装并正常运行SPO 1400 CRAFT软件&#xff08;以下简称LCT&#xff09; 条件2.确认已正确使用爱立信SPO 1400 CRAFT软件通过网络登录设备&#xff08;以下简称NE&#xff09; 具体登录…

Python通过HiperMATRIX API写数据

PyCharm编程和调试 代码片段 import random, time, requests, jsonhipermatrix_api_url http://192.168.1.240:9030/api/edge-management/equipment/7123766549772505088/write/values hipermatrix_login_url "http://192.168.1.240:9000/login/v1" send_interva…

【图解大数据技术】Flume、Kafka、Sqoop

【图解大数据技术】Flume、Kafka、Sqoop FlumeFlume简介Flume的应用场景 KafkaKafka简介Kafka架构Flume与Kafka集成 SqoopSqoop简介Sqoop原理sqoop搭配任务调度器实现定时数据同步 Flume Flume简介 Flume是一个数据采集工具&#xff0c;多用于大数据技术架构下的日志采集。 …

SQL-DCL(三)

一.DCL介绍 DCL英文全称是Data Control Language(数据库控制语言),用来管理数据库 用户,控制数据库的访问权限。 二.两个方面 1.数据库可以由那些用户访问 2.可以访问那些内容 三.DCL-管理用户 1.查询用户 USE mysql SELECT * FROM user 2.创建用户 CREATE USER…

基于Qwen2/Lllama3等大模型,部署团队私有化RAG知识库系统的详细教程(Docker+AnythingLLM)

自 ChatGPT 发布以来&#xff0c;大型语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff0c;大模型&#xff09;得到了飞速发展&#xff0c;它在处理复杂任务、增强自然语言理解和生成类人文本等方面的能力让人惊叹&#xff0c;几乎各行各业均可从中获益。 然…