算法练习——双指针

前言:大佬写博客给别人看,菜鸟写博客给自己看,我是菜鸟。

学前须知(对自己):这里的指针不一定指地址!也可能是数组下标。

1:移动零(双指针)

题目要求:

解题思路:

这道题的思路与先前快速排序中的lumoto法及其类似,只不过先前是通过双指针在数组中寻找基准值,这里是通过双指针将大于0的数排列在前。可以看到思路是完全相似的,不过是要求不一样。

在这里我们定义两个指针:

cur:当前指针的位置,初始值定义为0;

dest:需要替换数据的位置,初始值定义为-1;

让cur去遍历整个数组。

循环内,去寻找数组中不为0的数字

若满足条件(不为零),让dest++,随后与cur位置的数据进行交换。

若不满足条件,则让cur++,以此循环,直至cur遍历完整个数组。

代码实现:

    void moveZeroes(vector<int>& nums) {size_t dest = -1;size_t cur = 0;size_t n = nums.size()-1;while(cur <= n){if(nums[cur] != 0){dest++;swap(nums[cur],nums[dest]);}cur++;}}

2:复写零(双指针)

题目要求:

解题思路:

题目要求我们在原数组上进行修改,这说明一定涉及①:数据的交换②:数据向后平移。

此题要求复写零,那么不可能是数据的交换,则一定考虑数据的平移。但是仅仅从左往右遍历数组,然后平移数据,会丢失数组中原有的数据,因此需要从后往前遍历,以此平移数据同时添0

既然同样是双指针的思路,那么如何正确找到两个指针的起始位置呢?

同样以 cur 和 dest 来命名,他们的初始值分别为 0 ,-1;

当 cur 当前数据非零时,dest++;

当 cur 当前数据为零时,dest+=2;

然后判断当前 dest 是否已经超过或者等于数组长度,若满足则停止循环,不满足则cur++;

如果仅仅是题目中的案例,上述过程已经能正确找到 cur 和dest的位置,但有一种情况例外,那就是:dest 超出了数组,这种情况需要单独考虑。

已如下数组为例:[8,4,5,0,0,0,0,7];

如果按照上述过程,最终 cur 和 dest 的位置会来到如图所示处。

此时 dest 已经越过数组,因此我们所要作的,就是:将 dest -1 的位置置为 0,

同时将 dest -= 2, cur--即可。

注:思考为什么将dest -1 置为 0?

答:之所以为产生越界,是因为 cur 位置当前数据为0,因此 dest 多加了一次,才会导致越界,说明 dest 与 dest +1 的位置原本都应该为 0,但是数组越界了,所以只有dest位置的数据为0,因此需要将数组末尾置为0。

简单来说,针对越界这种特殊情况,我们手动进行了一次操作,即在数组末尾插入了零。

代码实现:

void duplicateZeros(vector<int>& arr) {int cur = 0, dest = -1;size_t n = arr.size() - 1;while(cur <= n ){if(arr[cur] == 0){dest += 2;}else{dest++;}if(dest >= n){break;}cur++;}if(dest > n){arr[dest-1] = 0;dest -=2;cur--;}while(cur >= 0){if(arr[cur] != 0){arr[dest--] = arr[cur--];}else{arr[dest--] = 0;arr[dest--] = 0;cur--;}}}

3:快乐数(快慢指针)

题目要求:

解题思路:

开始讲解思路前,需要知道一点:快乐数与先前链表中通过快慢指针确定链表是否为环形链表类似。一个数。经过多次运算后,始终会等于先前运算过的一个数,然后依次往复循环。

这里我们定义:

Num_Square()  函数:计算每位平方数的和

slow:调用一次上述函数

fast:调用两次上述函数

代码实现:

 int Num_Square(int n){int sum = 0;while(n){sum += pow(n%10,2);n /= 10;}return sum;}bool isHappy(int n) {int slow = n;int fast = Num_Square(n);while(slow != fast){slow = Num_Square(slow);fast = Num_Square((Num_Square(fast)));}if(slow == 1){return true;}else{return false;}}

4:装最多的水(双指针+单调性)

题目要求:

解题思路:

遇事不决,暴力思路,那显然是不可能的,因为会超时间复杂度。单纯使用前面的双指针算法似乎也不可行。

问:那这里该如何解决,才能优化时间复杂度呢?

答:这里不仅仅需要双指针,还需要结合单调性去考虑问题。

我们先定义两个指针:

left:初始值为0,指向数组最左端。

right:初始值为 n-1,n为数组元素个数,指向数组最右端。

现在想想水的容积是怎么算的?

容积 = 两边最短的高度 × 长度(right - left);

结合单调性,不难想到,如果此时 right--,left不变,那么任何数乘以 1 , 都会小于原来的容积(因为高度不变,长度减小),因此我们只需记录目前的最大值,然后left++,这样就减少了遍历次数,提升了效率。

此时 left = 1,right = n -1,同样结合上述单调性来看,如果left++,right 不变,那么任何数乘以 7,都会小于原来数(同样是因为高度不变,长度减小),因此这次我们需要将 right--,left不变,这样就减少了遍历次数,提升了效率。

总结:通过双指针+单调性去考虑这个问题

起始时:left = 0,right = n-1;

容积 = min(arr[left],arr[right]) × (right - left);

高度固定时,容积与长度为反比,记录此时容积大小,并改变较小的高度(left就++,right就--)

最终比较记录的容积值即可。

代码实现:

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

5:有效三角形个数(双指针+单调性)

题目要求:

解题思路:

通常我们通过判断三次两边之和小于第三边来确定三个数是否能够构成三角形。

问:怎么做?才能够减少判定次数,同时又能确定可以构成三角形?

答:用较小的两个边的和与最大的边进行比较,这样只需判断一次。

问:那如何找最大值呢?

答:遍历数组去找肯定是不可取的,那么就会想到,使用库函数的排序,这样最大值一定在数组末尾。

在有了上述的考虑后,本题同样是双指针+单调性去思考问题,无非就是条件不同。

以下列数组为例分析:

图一:

 arr[left] + arr[right] < max  不构成三角形,那么比 arr[right] 小的数都不构成三角形,因此不用遍历,left++

图二:

 arr[left] + arr[right] > max 构成三角形,那么此时只要比 arr[left] 大的数,都能够构成三角形,因此无需遍历,直接记录一共可以构成三角行的个数 n = right - left。记录完后,right--

图三:

此时,又回到了图一的情况,不多赘述,left++。

直至不满足 left < right ,此时需要更新 max,left 和 right 重新循环上述过程,知道 max = arr[2];

代码实现: 

int triangleNumber(vector<int>& nums) {sort(nums.begin(),nums.end());size_t n = nums.size() -1;int total = 0;while(n >=2){int left = 0;int right = n-1;while(left < right){if(nums[left] + nums[right] <= nums[n]){left++;}else{total += right - left;right--;}}n--;}return total;}

6:和为 s 的两个数字(双指针+单调性)

题目要求:

解题思路:

几乎与5是一样的,只不过5中是与数组中的元素比,而这里是与给定的数比,因此更简单。

大致思路:

考虑双指针(left,right),因为数组是有序数组,从单调性考虑。

若 arr[left] + arr[right] > target 说明太大了,right--

若 arr[left] + arr[right] < target 说明太小了,left++

代码实现:

vector<int> twoSum(vector<int>& price, int target) {int left = 0;int 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};}

7:三数之和

题目要求:

解题思路:

仍就是通过双指针+单调性的思路去解决这道问题,但区别在于,我们不能够输出重复的答案,因此这里还多了一个判重的过程。

如图定义来进行模拟运算:

当 arr[left] + arr[right] < arr[min] 时,left++;

当 arr[left] + arr[right] >arr[min] 时,right--;

当 arr[left] + arr[right] = arr[min] 时,left++;如图所示

此时需将答案保存到临时容器中,并让left++,right--,同时需要去重,即:

当 arr[left] == arr[left-1]时,left++;

当 arr[right] == arr[right+1]时,right--;

同时需要注意越界,即在循环过程中,left 始终小于 right

当 left 大于 right时结束一轮循环时,此时 min++,也要去重,与left 与 right 的去重思路一致,同时注意越界,min 始终小于容器内数据个数

代码实现:

vector<vector<int>> threeSum(vector<int>& nums) {int min = 0;vector<vector<int>> tmp;sort(nums.begin(),nums.end());//让数组变得有序方便使用单调性while(min < nums.size() && nums[min]<= 0){int left = min+1;int right = nums.size() - 1;while(left < right){int sum = nums[left] + nums[right];if(sum + nums[min] > 0){right--;}else if(sum + nums[min] < 0) {left++;}else{tmp.push_back({nums[min],nums[left++],nums[right--]});while(left < right && nums[left] == nums[left - 1]){left++;}while(left < right && nums[right] == nums[right + 1]){right--;}}}min++;while(min < nums.size() && nums[min] == nums[min-1]){min++;}}return tmp;}

8:四数之和

题目要求:

解题思路:

四数之和与三数之和的思路极其类似,无非就是多了一个需要固定的数,以及多一层循环,本质没有太大区别。

如图所示,固定 min 和 min1 去移动 left 和 right

当 arr[min] + arr[min1] + arr[left] + arr[right] < target时,left++

当 arr[min] + arr[min1] + arr[left] + arr[right] > target时,right--

当 arr[min] + arr[min1] + arr[left] + arr[right] = target时,

将当前  arr[min] ,arr[min1] , arr[left] , arr[right] 插入临时容器中

left++,right--,再判重,并注意越界

当最内层 left 和 right 循环结束时,min1++,并判重注意越界,开始下一轮循环

当 min1 等于倒数第三个下标时,第二层循环的首轮结束,min++,并判重,以此往复,直至循环结束。

代码实现:

vector<vector<int>> fourSum(vector<int>& nums, int target) {int min = 0;int n = nums.size()-1;vector<vector<int>> tmp;sort(nums.begin(),nums.end());while(min <= n-3){int min1 = min+1;while(min1 <= n-2){int left = min1+1;int right = nums.size() - 1;while(left < right){long sum = nums[left] + nums[right];long sum1 = nums[min] + nums[min1];if(sum + sum1 > target){right--;}else if(sum + sum1 < target) {left++;}else{tmp.push_back({nums[min],nums[min1],nums[left++],nums[right--]});while(left < right && nums[left] == nums[left - 1]){left++;}while(left < right && nums[right] == nums[right + 1]){right--;}}}min1++;while(min1 <= n-2 && nums[min1] == nums[min1-1]){min1++;}}min++;while(min <= n-3 && nums[min] == nums[min-1]){min++;}}return tmp;}

9:总结

双指针算法:

👉:这里的指针不是地址,也许是数组下标

👉:经常和单调性挂钩,有时候是根据数组长度判断单调(4),有时候是根据数组数据大小判断单调(5、6、7),根据数组数据判断单调可能需要将数组先排序,再做运算

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue3实现一个无缝衔接、滚动平滑的列表自动滚屏效果,支持鼠标移入停止移出滚动

文章目录 前言一、滚动元素相关属性回顾一、实现分析二、代码实现示例&#xff1a;2、继续添加功能&#xff0c;增加鼠标移入停止滚动、移出继续滚动效果2、继续完善 前言 列表自动滚屏效果常见于大屏开发场景中&#xff0c;本文将讲解用vue3实现一个无缝衔接、滚动平滑的列表自…

vscode翻译插件

vscode翻译插件 需求 &#xff1a; 在编写代码的时候&#xff0c; 打印或者定义变量的时候总是想不起来英文名称&#xff0c; 所有就开发了一款中文转换为英文的插件。 功能 1、目前支持选中中文&#xff0c;右键选择打印或者变量进行转换。 2、目前支持选中中文&#xff0…

Linux -- 初识线程

目录 线程的初步认识 为什么需要线程 怎么让代码分成多个执行流并发执行呢&#xff1f; 管理线程 线程的初步认识 线程是进程内部的一个执行分支&#xff0c;线程是CPU调度的基本单位。 在Linux操作系统中&#xff0c;线程是程序执行流的最小单位。一个进程可以包含多个线…

基于IM场景下的Wasm初探:提升Web应用性能|得物技术

一、何为Wasm &#xff1f; Wasm&#xff0c;全称 WebAssembly&#xff0c;官网描述是一种用于基于堆栈的虚拟机的二进制指令格式。Wasm被设计为一个可移植的目标&#xff0c;用于编译C/C/Rust等高级语言&#xff0c;支持在Web上部署客户端和服务器应用程序。 Wasm 的开发者参…

Linux SSH免密登入以及配置脚本

一、ssh原理简单介绍 客户端生成一对公钥和私钥&#xff0c;并将自己的公钥发送到服务器上 其中公钥用来加密&#xff0c;私钥用来解密。 二、ssh免密登入实现步骤详解 我这就以服务器controller和客户端compute来做为例子 2.1、首先在controller上输入ssh-keygen -t rsa …

Flutter 获取照片权限的时候是否要获取存储权限?

获取存储权限 Permission.storage.request(); 获取照片权限通常意味着访问相册&#xff0c;而访问相册可能还需要外部存储权限&#xff0c;因为照片通常存储在设备的外部存储中。所以&#xff0c;当你请求照片权限时&#xff0c;你也需要检查并请求外部存储权限。 是不是所有…

一个基于Rust适用于 Web、桌面、移动设备等的全栈应用程序框架

大家好&#xff0c;今天给大家分享一个用 Rust 语言编写的、受 React 启发的前端框架Dioxus&#xff0c;旨在为构建跨平台的用户界面提供高效、高性能的解决方案。 项目介绍 Dioxus项目的诞生源于开发者们对于更高效、更灵活的跨平台UI解决方案的渴望。 随着技术的发展&#…

IntelliJ Idea设置自定义快捷键

我IDEA的快捷键是自己修改成了和Eclipse相似&#xff0c;然后想要跳转到某个方法的上层抽象方法没有对应的快捷键&#xff0c;IDEA默认的是Ctrl U &#xff08;Windows/Linux 系统&#xff09; 或 Command U &#xff08;Mac 系统&#xff09;&#xff0c;但是我的不起作用&a…

前深度学习时代-经典的推荐算法

参考自《深度学习推荐系统》—— 王喆&#xff0c;用于学习记录。 1.协同过滤 “协同过滤”就是协同大家的反馈、评价和意见一起对海量的信息进行过滤&#xff0c;从中筛选出目标用户可能感兴趣的信息的推荐过程。 基于用户相似度进行推荐的协同过滤算法 UserCF 用户相似度…

达梦数据守护集群_动态增加实时备库

目录 1、概述 2、实验环境 2.1环境信息 2.2配置信息 2.3 查看初始化参数 3、动态增加实时备库 3.1数据准备 3.2配置新备库 3.3动态增加MAL配置 3.4 关闭守护进程及监视器 3.5修改归档&#xff08;方法1&#xff1a;动态添加归档配置&#xff09; 3.6 修改归档&…

6.qsqlquerymodel源码分析

目录 继承关系入口浅析qsqlquery刷新数据 扩展列或者移除列以及取别名读取数据与增减行读取数据 下一章节&#xff1a;如何使用qsqlquerymodel 与 qtableview实现自定义表格 继承关系 qsqlquerymodel 继承与qabstracttablemodel 入口 负责填充数据 void QSqlQueryModel::s…

【JavaEE】认识线程

一、引入线程 在任务管理器界面可以看到很多进程&#xff0c;可以利用CPU多核心这一点来更有效的执行这些进程&#xff0c;即在处理过程中将CPU的多个时间片分配给每个进程&#xff0c;这样的 "多进程编程"就可以起到并发编程的效果&#xff0c;因为进程可以被调度到…

qt QStatusBar详解

1、概述 QStatusBar是Qt框架提供的一个小部件&#xff0c;用于在应用程序窗口底部显示状态信息。它可以显示一些固定的文本和图标&#xff0c;并且可以通过API动态更新显示内容。QStatusBar通常是一个水平的窗口部件&#xff0c;能够显示多行文本内容&#xff0c;非常适合用于…

【Ubuntu】ubuntu 22.04 设置 Xorg 弃用 Wayland

# 编辑gdm3配置文件 sudo vim /etc/gdm3/custom.conf # 重启gdm3 sudo systemctl restart gdm3方式一&#xff1a;如果进行上述操作后&#xff0c;有对比度严重错误问题&#xff0c;请执行下列命令 # 安装驱动 sudo ubuntu-drivers autoinstall 方式二&#xff1a;推荐尝试方式…

日语学习的难易程度

日语学习的难易程度是一个相对主观的问题&#xff0c;它受到多种因素的影响&#xff0c;包括个人的语言学习能力、学习方法、学习时间、学习资源的可获得性以及个人对日语文化的兴趣和投入程度等。以下是对日语学习难易程度的一些分析&#xff1a; 优点与易学之处 文字系统&am…

WPF 打包

打包为单个exe文件直接运行 - - -版本.NET8 新建WPF项目 右键 - 发布 选择发布文件夹 选择发布文件夹 选择发布文件夹 配置 配置,保存 发布 WPF 打包为exe安装程序 示例 实现思路 引导项目中嵌入其它项目可运行目录的zip引导项目中解压zip文件到指定文件夹是…

RTC精度及校准

RTC精度偏差&#xff1a; RTC的基准时间和精度与石英晶体的频率相关&#xff0c;晶体的谐振频率取决于温度&#xff0c;因此RTC性能与温度相关&#xff0c;晶体的频率偏差是晶体正常频率的温度反转函数。 一、硬件方面&#xff1a; 1.使用高精度振荡器的RTC模块&#xff1b; …

C++ Qt6 QtQuick/QML入门进阶与项目实战视频教程

课程介绍 C Qt这些年在PC客户端、嵌入式、汽车座舱仪表等领域应用很广泛&#xff0c;例如剪映专业版、微信4.0、亿图脑图、Steam、美图秀秀、腾讯会议、钉钉(部分模块)等都是使用C QWidget/QML开发。特别是QML这种声明式UI开发更加快捷&#xff0c;QML的界面开发效率相对于QWid…

Linux:防火墙和selinux对服务的影响

1-1selinux 1-1 SELinux是对程序、文件等权限设置依据的一个内核模块。由于启动网络服务的也是程序&#xff0c;因此刚好也 是能够控制网络服务能否访问系统资源的一道关卡。 1-2 SELinux是通过MAC的方式来控制管理进程&#xff0c;它控制的主体是进程&#xff0c;而目标则是…

逻辑回归处理非线性关系与支持向量机的性能对比

逻辑回归是一种常用的线性分类方法&#xff0c;通常用于处理线性关系的二分类任务。但是&#xff0c;对于非线性问题&#xff0c;传统的逻辑回归模型可能表现不佳&#xff0c;因为它假设数据可以被一个线性决策边界分割开来。为了使逻辑回归能够处理非线性关系&#xff0c;我们…