双指针 — 复写零

目录

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.rhkb.cn/news/450355.html

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

相关文章

【白话文通俗易懂搞明白并解决】跨域问题

文章目录 跨域出现的场景跨域的定义解决跨域的方法方法一&#xff1a;JSONP方法二&#xff1a;添加响应头方法三&#xff1a;通过nginx代理跨域 跨域过滤器代码示例 跨域出现的场景 在前后端分离项目中&#xff0c;经常会出现跨域问题&#xff0c;表现为&#xff1a; 当在浏览…

拟声 0.37.0 | 拟物风格,超级优美,功能丰富

拟声是一款功能丰富的音视频播放器&#xff0c;支持多种音频来源&#xff0c;并具备独特的歌词弹幕、音源转换、跨设备共享与控制等功能。其创新的LRC歌词编解码器和新拟物风格的UI设计为用户提供了一个全新的视听体验。 大小&#xff1a;36M 百度网盘&#xff1a;https://pan…

7.存储过程中的事务管理(7/10)

1.引言 在现代信息技术快速发展的今天&#xff0c;数据库已经成为存储和管理数据的核心工具。无论是企业级应用、电子商务平台还是个人项目&#xff0c;数据库都扮演着不可或缺的角色。在这些应用中&#xff0c;数据的完整性、一致性和可靠性是至关重要的。这就引出了数据库事…

vue3--通用组件 popup 封装

在业务场景中,假设这里我们要实现点击 汉堡 后,会有一个自下而上的popup弹出层 因此这里我们需要先实现这样的一个公共的popup弹出层 那么我们这里的popup弹出层需要具备以下能力: 当popup展开时,内容视图应该不属于任何一个组件内部,而应该直接被插入到body下,这里需要…

【C++11】可变模板参数详解

个人主页&#xff1a;chian-ocean 文章专栏 C 可变模板参数详解 1. 引言 C模板是现代C编程中一个非常强大且灵活的工具。在C11标准中&#xff0c;引入了可变模板参数&#xff08;variadic templates&#xff09;&#xff0c;它为模板编程带来了革命性改变。它的出现允许我们…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,1-8

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

FlinkCDC 实现 MySQL 数据变更实时同步

文章目录 1、基本介绍2、代码实战2.1、数据源准备2.2、代码实战2.3、数据格式 1、基本介绍 Flink CDC 是 Apache Flink 提供的一个功能强大的组件&#xff0c;用于实时捕获和处理数据库中的数据变更。可以实时地从各种数据库&#xff08;如MySQL、PostgreSQL、Oracle、MongoDB…

【云岚到家】-day07-5-实战项目-优惠券活动-活动管理

【云岚到家】-day07-5-实战项目-优惠券活动-活动管理 2 优惠券活动管理2.1 需求分析2.1.1 **新增优惠券活动**1&#xff09;界面原型2&#xff09;数据分析3&#xff09;数据校验 2.1.2 **查询优惠券活动**1&#xff09;界面原型 2.2.3 **修改优惠券活动**1) 界面原型2&#xf…

Qt-窗口对话框QMessageBox的使用(51)

目录 前言 描述 使用 自定义按钮 简单方式创建 前言 Qt 提供了多种可复⽤的对话框类型&#xff0c;即 Qt 标准对话框。Qt 标准对话框全部继承于 QDialog类。常⽤标准对话框如下&#xff1a; 描述 消息对话框 QMessageBox 消息对话框是应⽤程序中最常⽤的界⾯元素。消息…

D3.js(五):实现组织架构图

实现组织架构图 效果初始化组织机构容器并实现缩放平移功能效果源码 渲染节点效果源码 渲染连线效果源码 完整源码 效果 初始化组织机构容器并实现缩放平移功能 效果 源码 import {useEffect} from react; import TreeData from ./json/tree-data.json;interface ITreeConfig…

crd介绍

在 Kubernetes 中&#xff0c;CRD&#xff08;Custom Resource Definition&#xff09;和 CR&#xff08;Custom Resource&#xff09;是用于扩展 Kubernetes 功能的机制。它们的关系和使用可以用一个完整案例来说明。 定义 CRD&#xff08;Custom Resource Definition&#x…

中后台 B 端产品设计

中后台 B 端产品设计 一、设计目标二、设计流程三、设计要点四、相关模块 叮嘟&#xff01;这里是小啊呜的学习课程资料整理。好记性不如烂笔头&#xff0c;今天也是努力进步的一天。一起加油进阶吧&#xff01; 中后台B端产品设计&#xff1a; 是指针对企业内部业务人员和管理…

python+appium+雷电模拟器安卓自动化及踩坑

一、环境安装 环境&#xff1a;window11 1.1 安装Android SDK AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 这里面任选一个就可以&#xff0c;最终下载完主要要安装操作安卓的工具adb&#xff0c;安装这个步骤的前提是要…

Linux驱动开发——设备树

文章目录 1 什么是设备树&#xff1f;2 DTS、DTB和DTC3 DTS语法3.1 dtsi头文件3.2 设备节点3.3 标准属性3.4 根节点compatible属性3.5 向节点追加或修改内容 4 创建小型模板设备树5 设备树在系统中的体现6 绑定信息文档7 设备树常用OF操作函数7.1 查找节点的OF函数7.2 查找父/子…

【工具变量】上市公司当年是否发生财务重述指标整理Stata代码(2000-2023年)

计算说明&#xff1a;使用财务重述公告中所更正年报对应的年度作为财务重述的年度&#xff0c;若企业年报中发生财务重述取1&#xff0c;否则取0。本示例的财务重述是指上市公司对以前年度财务报表中的会计差错进行更正和披露&#xff0c;不包括股票拆分、股票红利、终止经营、…

Java 类和对象详解(上 )

个人主页&#xff1a; 鲤鱼王打挺-CSDN博客 Java专栏&#xff1a;https://blog.csdn.net/2401_83779763/category_12801101.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12801101&sharereferPC&sharesource2401_83779763&sharefromfrom_link &…

SwiftUI 如何取得 @Environment 中 @Observable 对象的绑定?

概述 从 SwiftUI 5.0&#xff08;iOS 17&#xff09;开始&#xff0c;苹果推出了全新的 Observation 框架。它作为下一代内容改变响应者全面参与到数据流和事件流的系统中。 有了 Observation 框架的加持&#xff0c;原本需要多种状态类型的 SwiftUI 视图现在只需要 3 种即可大…

R语言详解predict函数

R语言中predict函数在建立模型&#xff0c;研究关系时常用。但是不同type得到的结果常常被混为一谈&#xff0c;接下来&#xff0c;探讨predict得到的不同结果。 #数据 set.seed(123) n<-1000 age<-rnorm(n,mean50,sd10) gender<-rbinom(n,1,0.5) disease<-rbinom…

CDC变更数据捕捉技术是什么?和ETL有什么不同?

一、什么是CDC技术? 变更数据捕获&#xff08;Change Data Capture&#xff0c;简称 CDC&#xff09;是一种用于识别和跟踪数据源中发生变化的数据的技术。 工作原理&#xff1a; 1.监测数据源&#xff1a;CDC 工具会持续监测指定的数据源&#xff0c;如数据库表、文件系统…

【踩坑随笔】Tensorflow-GPU训练踩坑

一个无语的坑&#xff0c;4060单卡训练&#xff0c;8G内存本来就不够&#xff0c;还没开始训练就已经爆内存了&#xff0c;但是居然正常跑完了训练&#xff0c;然后一推理发现结果就是一坨。。。往回翻日志才发现原来中间有异常。 首先解决第一个问题&#xff1a;Could not lo…