【基础算法总结】双指针
目录
- 一,双指针算法介绍
- 二,算法原理和代码实现
- 283.移动零
- 1089.复写零
- 202.快乐数
- 11.盛最多水的容器
- 611.有效三角形的个数
- LRC179.和为s的两个数
- 15.三数之和
- 18.四数之和
- 三,算法总结
一,双指针算法介绍
双指针算法是基础算法之一,一般用于涉及数组分块/数组划分这类问题 。这里的"指针"是利用数组下标或是一个数来充当的。
在遍历过程中,两个指针的位置:
cur:从左往右扫描数组,遍历数组。
dest:指向已处理的区间内,非0元素的最后一个位置。如下图
所以两个指针把数组分成了三个区间:
二,算法原理和代码实现
283.移动零
算法原理:
我们也是定义两个变量 cur 和 dest,根据上面介绍的两个指针的位置,初始化 cur = 0, dest = -1。
在 cur 从前往后遍历的过程中,无非两种情况:
1. 遇到0元素:cur++
2. 遇到非0元素:先swap(dest+1, cur), 再cur++, dest++
代码实现:
class Solution
{
public:void moveZeroes(vector<int>& nums) {for(int cur = 0, dest = -1; cur < nums.size(); ){if(nums[cur] == 0) cur++;else swap(nums[dest+1], nums[cur]), cur++, dest++;}}
};
1089.复写零
算法原理:
这道题看起来简单,但是有很多坑,很多细节。我们使用双指针先在草稿纸上模拟,不难发现从前往后复写是不行的,会覆盖后面的数据。但是要如何从后往前复写呢,起始位置怎么确定?
所以解决这个题有两个步骤:
1. 先找到最后一个复写的数。
这一步骤也要用双指针算法:
当走完这个双指针,此时 cur 指向的数就是最后一个要复写的数,dest 指向的位置就是开始复写的第一个位置。
2. 再从后往前进行复写操作:
在 cur 从后往前遍历的过程中,无非两种情况:
(1) 遇到0元素:dest向前复写两个0,cur–,dest -= 2
(2) 遇到非0元素:先arr[dest] = arr[cur], 再cur++, dest++
细节/技巧问题:
(1) 在第一步的第三小步中,一定要先判断 dest 是否已经结束
(2) 还要处理一种特殊情况:[1,0,2,3,0,4]。根据第一步的双指针,此时dest会越界,就要做特殊处理,当 dest == n 时,直接把 arr[n-1] = 0, cur–, dest -= 2。
代码实现:
class Solution
{
public:void duplicateZeros(vector<int>& arr){// 找到最后一个要复写的位置int cur = 0, dest = -1, n = arr.size();while (cur < n){if (arr[cur]) dest++;else dest += 2;if (dest >= n - 1) break;cur++; // 注意每次++之前都要先判断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 - 1] = 0;dest -= 2;cur--;}}}
};
下面是一开始我写的错误代码,以示警戒:
class Solution
{
public:void duplicateZeros(vector<int>& arr){// 先找到最后一个要复写的数int n = arr.size();int cur = 0, dest = -1;for (; dest < n - 1;){if (arr[cur] != 0) dest++;else dest += 2;if (dest != n - 1)cur++;}// 处理边界情况if (dest == n) arr[n - 1] = 0, cur--, dest -= 2;// 从后往前完成复写操作for (; cur >= 0 && dest >= 0; ){if (arr[cur] != 0) arr[dest] = arr[cur], dest--;else arr[dest] = 0, arr[dest - 1] = 0, dest -= 2;cur--;}}
};
202.快乐数
算法原理:
首先来理解题目:
所以这道题可以抽象成另类的 “链表是否带环问题”。只不过这道题是一定带环的,只要根据环内的数进行判断即可。
使用经典的快慢双指针算法:
(1) 定义快慢指针
(2) 慢指针每次向后走一步,快指针向后走两步
(3) 判断相遇时候的值即可
拓展:为什么这个题一定成环?
使用鸽巢原理证明。
细节/技巧问题:
(1) 这题的双指针不再是数组下标,而是一个数。
(2) 在进入第一次循环时,先让 fast 指向第二个数,不然进不了循环。
代码实现:
class Solution
{
public:bool isHappy(int n) {int slow = n, fast = mypow(n);while(slow != fast){slow = mypow(slow);fast = mypow(mypow(fast));}if(slow == 1)return true;elsereturn false;}int mypow(int n){int sum = 0;while(n){int a = n % 10;n /= 10;sum += a*a;}return sum;}
};
11.盛最多水的容器
算法原理:
解法1:暴力枚举,O(N*N)。这应该是大家心中第一个闪过的想法,就是使用两层for循环计算出全部体积,再求出最大值。但是这个解法超时。
解法2:利用单调性,使用双指针,O(N)
首先观察一个规律:
我们固定一个数,再向外枚举,会出现下面的情况,结果都是缩小,就可以把这个数直接抹去,避免无用枚举。
所以可以把这个规律推广到全部数据:
定义两个指针指向开头和结尾,此时可以计算出一个体积,根据上面的规律把较小的那个高度直接舍去,指针向里缩,继续计算体积…,直到两个指针相遇,统计出最大体积。
细节/技巧问题:
体积= 高度 * 宽度。根据木桶原理,高度是小的那个,而宽度是两个下标相减。
代码实现:
class Solution
{
public:int maxArea(vector<int>& height) {int cur1 = 0, cur2 = height.size() -1;int maxV = 0;while(cur1 <= cur2){int H = min(height[cur1], height[cur2]);maxV = max(maxV, (cur2 - cur1) * H);// 利用单调性if(height[cur1] < height[cur2]) cur1++;else cur2--;}return maxV;}
};
611.有效三角形的个数
算法原理:
首先补充一个数学知识,只要一次判断,得出是否能构成三角形:
若a <= b <= c 且 a +b > c,则可以构成三角形。
所以可以得出一个优化:先把数组排序,O(N*logN)。
数组排完序后:
解法1:暴力枚举,O(N*logN + N^3),三层for循环,绝对超时。
解法2:利用单调性,使用双指针算法,O(N*logN + N^2)。
(1) 先用c固定最大的元素,定义left 和right分别指向第一个元素,c的下一个元素。
(2) 若nums[left] + nums[right] > nums[c],由于left和 right之间的数都比 nums[left] 大,与 nums[right] 相加后一定大于c,构成三角形了,就不要一个个枚举了,直接right- -。
(3) 此时三角形个数 = right - left。
(4) 若nums[left] + nums[right] <= nums[c],由于left和 right之间的数都比 nums[right] 小,与 nums[left] 相加后一定小于c,也不要一个个枚举了,直接left++。
(5) 以上是走完一趟的个数,再c–,固定下一个数。
代码实现:
class Solution
{
public:int triangleNumber(vector<int>& nums) {// 排序sort(nums.begin(), nums.end());int n = nums.size();int ret = 0; // 记录个数for(int c = n-1; c >= 2; c--){int left = 0, right = c - 1;while(left < right){if(nums[left] + nums[right] > nums[c]){ret += right - left;right--;} else left++;}}return ret;}
};
LRC179.和为s的两个数
算法原理:
解法1:暴力枚举,两层for循环,O(N^2),绝对超时。没有好好利用单调递增这个特性!!
解法2:和上一题的解法2类似,也是利用单调性和双指针算法,O(N)。
细节/技巧问题:
找到数对后就直接 break 结束循环。
代码实现:
class Solution
{
public:vector<int> twoSum(vector<int>& price, int target) {vector<int> ret;int left = 0, right = price.size() - 1;while(left < right){int sum = price[left] + price[right];if(sum > target) right--;else if(sum < target) left++;else {ret.push_back(price[left]); ret.push_back(price[right]);break;}}return ret;}
};
15.三数之和
算法原理:
解法1:排序+暴力枚举+利用set去重,O(N^3).
解法2:排序+双指针,O(N^2).
这道题其实是上一题的进阶版,首先排序,再固定一个数 a,在该数后面的区间内使用双指针算法快速的找到两个数的和是 -a。
难点/细节/技巧:
这道题的算法不难想,难的是保证不重不漏。
(1) 去重操作有两个方面:一是找到一种结果后,left 和 right 指针要跳过重复元素,二是当使用完一次双指针后,i 也需要跳过重复元素,此时要注意越界。
(2) 还有一个小优化就是当固定的那个数是正数时,后面再也找不到两数和为负数了,直接结束。
代码实现:
class Solution
{
public:vector<vector<int>> threeSum(vector<int>& nums){// 排序sort(nums.begin(), nums.end());vector<vector<int>> vv;int n = nums.size();for(int i = 0; i <= n-3; i++) // 固定数 a{if(nums[i] > 0) break; // 小优化int left = i +1, right = n-1, a = nums[i];// 双指针while(left < right){int sum = nums[left] + nums[right];if(sum > -a) right--;else if(sum < -a) left++;else {vv.push_back({nums[i], nums[left], nums[right]});// 去重,注意越界while(left < right && nums[left] == nums[left+1]) left++;while(left < right && nums[right] == nums[right-1]) right--;left++;right--;}}while(i < n-1 && nums[i] == nums[i+1]) i++;}return vv;}
};
18.四数之和
算法原理:
本道题又是上一题的进阶版。
难点/细节/技巧:
这道题会出现数据溢出的风险。所以当计算两个固定是数之和时,类型定义为 long long。
其余细节参考上一题。
代码实现:
class Solution
{
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {// 排序sort(nums.begin(), nums.end());int n = nums.size();vector<vector<int>> vv;for(int i = 0; i < n; i++) // 固定数a{// 三数和for(int j = i+1; j < n; j++)// 固定数b{// 双指针算法int left = j+1, right = n-1;long long t = (long long)target - (nums[i] + nums[j]);while(left < right){long long sum = nums[left] + nums[right];if(sum > t) right--;else if(sum < t) left++;else{vv.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--;left++;right--;}}while(j < n-1 && nums[j] == nums[j+1]) j++;}while(i < n-1 && nums[i] == nums[i+1]) i++;}return vv;}
};
三,算法总结
双指针算法是一种基础,但是十分经典的算法。通过上面的若干道题可知,"双指针"使用起来是十分灵活的,有时代指数组下标,有时也可以代指一个数。使用双指针算法的关键之一就是要控制好边界,稍不留神就会出现数组越界的问题,并且在使用这个算法时强烈建议各位一定要多画图,光靠想象容易出错并且会忽略很多细节问题。
相关文章:

【基础算法总结】双指针
目录 一,双指针算法介绍二,算法原理和代码实现283.移动零1089.复写零202.快乐数11.盛最多水的容器611.有效三角形的个数LRC179.和为s的两个数15.三数之和18.四数之和 三,算法总结 一,双指针算法介绍 双指针算法是基础算法之一&am…...

教你制作一本一对一授权才能阅读的样本册
在这个信息时代,保护个人隐私变得越来越重要。一对一授权阅读机制,正是为了满足这一需求而诞生。今天,就让我来教你如何制作一本只有一对一授权才能阅读的样本册。这不仅适用于保护个人隐私,还能为企业、研究机构等提供安全、高效…...

【DEV工具-IDEA】idea的光标变成黑块了?
项目场景: 解决:windows:按一下insert键。...

没通过算法备案 或许是这几点你没做好
没通过算法备案 或许是这几点你没做好 当企业提交算法备案遭遇“不予通过”时,往往是因为一些看似微小却至关重要的细节未能达到标准。以下是一些常见的原因,希望能为准备备案的企业提供一些预警和指导: ICP备案缺失:互联网信息服…...
力扣172.阶乘后的0
class Solution {public int trailingZeroes(int n) {int ans 0;//本质:每5个数有一个5的倍数,每25个数有一个25的倍数……int num 5;while(n / num ! 0) {ans n/num;num * 5;}return ans;} }...

Oracle 19c数据库:Windows详细安装与配置指南
Oracle 19c的安装和配置是一个相对复杂但系统化的过程,本文演示如何在 Windows 系统下安装 Oracle数据库,安装足够的磁盘空间(一般需要5~6个G,所以选剩余空间大的盘)。以下是一个详细的步骤指南,包括准备工…...

解决职业摔跤手分类问题的算法与实现
解决职业摔跤手分类问题的算法与实现 引言问题定义算法设计二分图判定算法步骤伪代码C语言实现引言 在职业摔跤界,摔跤手通常被分为“娃娃脸”(“好人”)型和“高跟鞋”(“坏人”)型。在任意一对摔跤手之间,都有可能存在竞争关系。本文的目标是设计一个算法,用于判断是…...
微擎框架
微擎框架之——多级查询显示每项个数-Poison-CSDN博客微擎框架之——系统内置分页加载-CSDN博客 微擎-关于安装模块时出现:Could not resolve: www.52xbjs.com (Could not contact DNS servers)-Poison-CSDN博客...
容器化技术在非结构化数据中台的部署研究
随着大数据时代的到来,非结构化数据的处理与管理日益成为企业和组织关注的重点。为应对非结构化数据中台在数据存储、处理及安全性等方面面临的挑战,本研究深入探讨了容器化技术在该领域的部署与应用。研究首先概述了容器化技术的基本概念、特点及其在非…...
RK3399 android7.1 话柄电话功能
实现功能:挂柄接IO口+GND控制话机听筒与系统喇叭的切换(抬起手柄声音由喇叭切换到听筒,挂到磁吸底座喇叭出声) 应用场景: 电子电话班牌,电话机等 硬件接线方式: 电话手柄:听筒接耳机座子<HRP,GND>,麦克风接<MIC+,MIC-> 电话底座:磁吸座子接<IO2,GND&g…...
实习四十:部署project_exam_system项目——及容器的编排
(一)安装docker、编辑daemon.json文件、安装docker-compose编排容器、启动docker 1.环境准备 [rootdocker--1 ~]# rz -E rz waiting to receive. [rootdocker--1 ~]# ls anaconda-ks.cfg docker.sh [rootdocker--1 ~]# source docker.sh [r…...
栈,队列
栈(Stack)和队列(Queue)是两种常用的数据结构,它们在计算机科学中有着广泛的应用。它们的主要区别在于元素的添加和移除方式。 栈(Stack): 栈是一种后进先出(Last In F…...

新增一个数组传递给后端
实现的效果: 页面 <div style"margin-bottom: 10px" v-if"totalPrice"><p style"font-weight: bolder;margin-bottom: 10px">支付计划<el-button type"text" size"small" click"addPayInf…...

Flutter集成Firebase中的Realtime Analytics
实时分析(Realtime Analytics)的功能 实时数据更新:Firebase实时分析提供实时数据更新,让开发者可以实时了解应用程序的使用情况,包括活跃用户数量、事件触发次数等指标自定义事件跟踪:开发者可以通过自定…...

2024国赛数学建模A题B题C题D题E题思路资料模型
开始在本帖实时更新2024国赛数学建模赛题思路代码,文章末尾获取! 持续更新参考思路...

C语言字面量和常量
目录 引言 1. 字面量 1.1 字符字面量 1.2 整型字面量 1.3 浮点字面量 2. 常量 2.1 使用预处理器指令 #define 定义常量 2.1.1 语法格式 2.1.2 使用举例 2.2 使用 const 关键字定义常量 2.3 使用 #define 和 const 定义常量的区别 引言 看了一些博文,有的文…...

视频结构化从入门到精通——行为分析类应用
行为分析类应用 1. 认识行为分析 监控/判断视频画面中目标的运动过程、携带属性等。从数据中自动识别、跟踪和理解人类或物体行为。 1. 车的行为分析应用 车辆行为分析主要用于监控和管理车辆的动态行为,广泛应用于智能交通、城市管理和安全监控。关键应用包括&…...
Redis的KeyExpirationEventMessageListener键过期监听器
MessageListener通过监听key过期的Redis keyspace通知,然后通过ApplicationEventPublisher发布RedisKeyExpiredEvent事件的模式进行事件监听和广播。 redis.conf地址:https://github.com/redis/redis/blob/unstable/redis.conf Redis官方地址࿱…...

MP4视频压缩,推荐这五大压缩操作
MP4视频压缩,在当今数字化的时代,视频已经成为我们日常生活和工作中不可或缺的一部分。然而,随着视频分辨率和长度的增加,MP4文件的大小也变得越来越大,这不仅占用了大量的存储空间,还使得传输和分享变得困…...

docker 安装NextERP
有很多方式: 一 docker sudo docker run -itd -p 8016:80 -v ERPNext_db:/var/lib/mysql -v ERPNext_sites:/home/frappe/frappe-bench/sites --name ERPNext lvxj11/erpnext:latest二 git clone https://e.coding.net/yuanerp/yuanerp/frappe_docker.gitcp exa…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...

实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...

【QT控件】显示类控件
目录 一、Label 二、LCD Number 三、ProgressBar 四、Calendar Widget QT专栏:QT_uyeonashi的博客-CSDN博客 一、Label QLabel 可以用来显示文本和图片. 核心属性如下 代码示例: 显示不同格式的文本 1) 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别…...
java+webstock
maven依赖 <dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.3.5</version></dependency><dependency><groupId>org.apache.tomcat.websocket</groupId&…...