双指针 — 复写零

server/2024/10/18 0:03:43/

目录

1. 题目解析

2. 算法讲解

1.算法原理

2.“异地”操作

3.“就地”操作

误解

正解

从后往前完成复写操作 

找到最后一个复写的数

处理边界情况

3. 编写代码

正解顺序

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

2.处理边界情况

3.从后往前完成复写操作 

性能分析:

完整代码(大神版): 


1. 题目解析


1089.复写零 

题目中的限制条件:

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

如果这个题没有以上的限制条件,暴力解题会非常简单,但是因为这个限制条件,难度就直线上升了,并且容易被LeetCode给的难度迷惑;

以下图的数组为例:

根据题意,如果遍历到遇到0,就写两遍,其他数字写一遍,直到写满数组的固定长度为止。


2. 算法讲解


1.算法原理

解法:双指针算法

双指针算法并不是凭空来的,而是先根据“异地”操作,然后优化成双指针下的“就地”操作 

异地”操作:新创建一个数组,根据原数组和修改条件,对新数组进行相应的修改;

“就地”操作:在原有数组的基础上,根据修改条件,对原数组进行修改。


2.“异地”操作

如果我们先不管题目的限制条件,通过“异地”操作,完成对数组复写零,那么该怎么写呢 ?

我们定义两个指针,cur遍历原数组,dest遍历新数组;

cur从原数组0下标开始,dest从新数组0下标开始:

在遍历的过程中,

cur遇到非0元素,则dest在新数组上写一遍:

cur遇到  0元素,则dest在新数组复写两遍:

dest写好后,cur向后移动一位,dest再向后移动一位,直到dest走出数组为止:


3.“就地”操作

我们写完异地操作后,接下来,我们要在刚刚的基础上,模拟优化异地操作的就地操作。 

误解


刚开始,dest和cur两个指针,都指向数组0下标。 


当cur指向1时,cur和dest直接往后移动一位;cur指向0时,如果直接抄两遍,把1下标和2下标全改成0,这时候原来3下标的元素2就丢失了:

这是错的。所以,在原数组上,从左到右修改数组,是解决不了问题的,因为会覆盖后面的数据。


删除数组中值为val的题目,就是先通过异地操作模拟得到规律,再优化成就地操作双指针,发现就能直接解决该问题;(从要删除的val所在的元素下标开始,从后往前覆盖元素)

因为删除数组中值为val的元素这个题,dest肯定是在cur之后的,覆盖元素时,不会让除val以外的元素丢失,因此可以直接使用就地操作双指针完成;

但是,复写会覆盖原来的数据,因此不能直接从左向右修改数组,那我们试试从右往左修改数组。 


正解


从后往前完成复写操作 

从又向左修改数组,dest指向最后一个元素,cur指向最后一个复写的元素。以下图的数组为例:

(上图表示数组前后变化情况)

假设我们已经提前知道:通过复写零的规则,最后一个被复写的元素是4;

所以刚开始cur指向数组6下标的4元素,dest指向数组最后一个元素0:

接下来,我们要通过cur指向的元素的值,来让dest对数组作出相应的修改

当cur指向4,就修改dest指向的元素为4:

处理好后,让cur和dest同时向前移动一位:

cur遍历到的数组元素为0时,这时候,令dest指向的元素为0,并且cur先不动;dest再往前一步,再令dest指向的元素为0:

完成以上步骤后,再调整cur和dest同时向前移动一位

通过上面cur遍历到的元素,让dest作出对数组不同的修改方法,注意在dest修改好后,cur和dest都要往前移动一位,最后结果: 

整个过程,dest的调整都是基于cur遍历到的元素的值;刚开始cur的位置,为最后一个要写的元素的位置;

只要cur的位置找对了,我们就会发现:cur始终在dest之前;所以整个循环条件为cur>=0。

所以,以上就是第二步操作,从后往前完成复写操作,接下来,需要重点讲第一步操作,先找到最后一个复写的数


找到最后一个复写的数

要找到最后一个复写的数,可以用双指针算法 :

定义一个指针cur指向数组第一个元素,dest指向-1;

因为,刚开始,我们并不知道复写的最后一个数在哪,所以dest指向-1的位置,cur的作用是从前往后遍历数组,决定dest向后移动一步,还是两步。

循环结束条件dest<array.length(dest指向数组最后一个元素的位置即可结束循环)

 双指针算法

1.先判断 cur 位置的值;

2.决定 dest 向后移动一步(cur指向的元素非0)或者两步(cur指向的元素为0);

3.判断一下 dest 是否已经到数组最后一个元素的位置;

4.dest 还没有到数组最后一个元素的位置,cur++ 

此时,dest在数组最后一个元素的位置,cur指向的元素,就是最后一个被复写的数。 


处理边界情况

按照上面的两步,提交还是无法通过,因为会有一个特例:

 要找到上面数组中,最后一个复写的数:

我们可以看到,对于上面的一个特例,通过双指针算法,如下图:最后dest是无法停在数组最后一个元素的位置上的,dest会发生越界;在leetcode上,只要有用例发生越界,那么整个代码就无法通过。

 所以,cur没有问题的时候,dest是有可能出现问题的;因此,我们在从后往前复写的操作前,还要加一步操作,处理一下边界情况。


怎么处理边界情况呢?

出现越界的情况,一定是cur等于0,导致dest直接越界;

按照从后往前复写的操作,会把最后一个元素和dest指向的数组越界位置,都修改成0,这时候就越界访问了:

此时,不能把两个位置(length-1和length)都修改成0;

只需要把数组最后一个元素(length-1)修改成0即可

把length-1位置的值修改成0,然后,我们让dest从后往前走两步,cur从后往前移动一步,然后正常复写即可。(这是一种特殊的越界情况,直接单拎出来特殊处理)

接下来,我们再进行复写操作,就不会出现越界情况


3. 编写代码

正解顺序

我们同时可以看看小白和大神写的代码差别在哪里?

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

2.处理边界情况

3.从后往前完成复写操作 

按照小白的写法,哪怕知道这道题的解法,但是在leetcode上会因为代码的繁琐而超时(狗头):

 


性能分析:

简单分析一下时空复杂度:

对于时间复杂度,虽然这里有两个while循环,但是常数级别是可以忽略的,所以时间复杂度还是O(N)的,所以相当于,一次遍历数组,我们就完成了复写操作。

空间复杂度,这里只用了有限的三个变量,所以是O(1)


完整代码(大神版): 

class Solution {public void duplicateZeros(int[] arr) {int dest=-1,cur=0,n=arr.length;//1.找到最后一个复写的数while(dest<n){if(arr[cur]!=0){dest++;}else {dest+=2;}if(dest>=n-1)break;cur++;}//2.处理边界情况if(dest==n){arr[n-1]=0;cur--;dest-=2;}//3.从后往前完成复写操作 while(cur>=0){if(arr[cur]!=0){arr[dest--]=arr[cur--];}else {arr[dest--]=0;arr[dest--]=0;cur--;}}}
}

 


http://www.ppmy.cn/server/132625.html

相关文章

腾讯云直播录制相关

直播录制的原理是什么&#xff1f; 对于一条直播流&#xff0c;一旦开启录制&#xff0c;音视频数据就会被旁路到录制系统。主播的手机推上来的每一帧数据&#xff0c;都会被录制系统追加写入到录制文件中。 一旦直播流中断&#xff0c;接入层会立刻通知录制服务器将正在写入的…

Java - SpringBoot(基础)

SpringBoot基础 概述 SpringBoot是Spring提供的一个子项目&#xff0c;用于快速构建Spring应用程序。SpringBoot特性1.起步依赖 本质上就是一个Maven坐标&#xff0c;整合了完成一个功能需要的所有坐标。就是通过一个依赖&#xff0c;可以代替所有所需的依赖。2.自动配置 遵循…

TY1801 内置GaN电源芯片(18w-65w)

TY1801 是一款针对离线式反激变换器的多模式 PWM GaN 功率开关。TY1801内置 GaN 功率管,具备超宽 的 VCC 工作范围&#xff0c;非常适用于 PD 快充等要求宽输出电压的应用场合,TY1801不需要使用额外的绕组或外围降压电路&#xff0c;节省系统 BOM 成本。TY1801 支持 Burst&…

Docker新手必看:快速安装和配置BookStack在线文档系统

文章目录 前言1. 安装Docker2. Docker镜像源添加方法3. 创建并启动BookStack容器4. 登录与简单使用5. 公网远程访问本地BookStack5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 前言 本文主要介绍如何在Linux系统使用Docker本地部署在线文档管理…

网络爬虫中的几种数据存储方式(上篇)

本文的内容是介绍网络爬虫中的数据存储方式。大家都知道爬虫的最重要功能就是从网络中将数据提取出来。现在问题来了&#xff0c;那么提取出来的数据该何去何从&#xff0c;如果仅仅只是保存在内存当中&#xff0c;当程序结束后岂不是所有的内容都消失了&#xff1f;因此需要将…

Spring Boot框架下JavaWeb在线考试系统的创新实现

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

不同编程语言的隐式控制流分析:特性、利弊及代码示例

隐式控制流&#xff08;Implicit Control Flow&#xff09;是指代码的执行路径不显式暴露在源代码中&#xff0c;往往由多态、重载、接口等高级语言特性引发。尽管这些特性带来了灵活性&#xff0c;但它们也可能掩盖代码的实际执行路径&#xff0c;使调试变得更复杂。以下分析了…

视图使用控制器模板分配变量

文章目录 控制器视图路由配置控制器视图 视图使用控制器模板分配变量控制器视图 控制器视图 路由配置 <?phpuse Illuminate\Support\Facades\Route;/* |-------------------------------------------------------------------------- | Web Routes |---------------------…