当前位置: 首页 > news >正文

【双指针专题】之复写零

一、题目描述

给定一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移

要求:

  1. 请不要在超过该数组长度的位置写入元素。

  2. 请对输入的数组 就地 进行上述修改,不要返回任何东西。

示例 :

  • 输入:arr = [1,0,2,3,0,4,5,0]
  • 输出:[1,0,0,2,3,0,0,4]
  • 解释:调用函数后,输入数组被修改为 [1,0,0,2,3,0,0,4]

二、思路引导:双指针 / 反向复写

这一题的难点在于:如果我们从前往后复写,会导致后面的元素被覆盖掉


举个例子:当我们遇到 0 时,需要把它写两次,但后面本该还没来得及复写的数会被提前覆盖。

所以我们需要换一种思路:
从后往前复写!


那为什么可行呢?

  • 因为往后写的时候,后面的位置还没被“覆盖”,这样就能保证数据不会丢失。
  • 关键点是要先找到“最后一个能被复写的数”,然后再从后往前依次复写。

三、算法流程

这里我们用两个指针

  • cur:扫描数组的当前下标
  • dest:复写到结果数组的下标。

步骤 1:找到最后一个需要复写的数

初始化:cur = 0, dest = -1

  • 如果 arr[cur] != 0,则 dest++
  • 如果 arr[cur] == 0,则 dest += 2
  • 一旦 dest >= n - 1(即超过数组尾部),循环结束。

步骤 2:边界处理

如果 dest == n,说明最后一个位置正好是复写 0 导致的越界

  • arr[n - 1] 赋值为 0
  • cur--, dest -= 2 进行修正

步骤 3:从后往前复写

  • 如果 arr[cur] != 0,那么就复写一个元素:arr[dest--] = arr[cur--]
  • 如果 arr[cur] == 0,那么就复写两次:arr[dest--] = 0; arr[dest--] = 0; cur--;

四、代码实现(C++)

#include <vector>
#include <iostream>
using namespace std;class Solution {
public:void duplicateZeros(vector<int>& arr) {int dest = -1, cur = 0;// 步骤 1: 找到最后一个需要复写的数// 思路:如果是非零元素,结果数组只占 1 个位置;//       如果是 0,结果数组需要占 2 个位置。while (cur < arr.size()) {if (arr[cur]) ++dest;     // 非零元素,目标指针前进 1else dest += 2;  // 0 元素,目标指针前进 2if (dest >= arr.size() - 1) // 超过数组末尾,停止break;cur++;}// 思路 2: 边界处理// 如果刚好越界到数组的末尾 n(即多出了一位),// 说明最后一个元素是因为 0 的复写产生的。if (dest == arr.size()) {arr[arr.size() - 1] = 0; // 把最后一位补上 0dest -= 2;  // 回退两步cur--;      // 当前元素回退一步}// 思路 3: 从右往左复写// 反向扫描,避免元素被覆盖while (cur >= 0) {if (arr[cur]) {// 非零元素:正常复写一个arr[dest--] = arr[cur];} else {// 零元素:复写两次arr[dest--] = 0;arr[dest--] = 0;}cur--;}}
};

五、代码剖析与细节问题

1. 为什么要从后往前复写?

如果从前往后,一旦遇到 0,需要写两次,但此时后面的元素还没有被处理,就会被覆盖掉。
所以必须从后往前复写,这样可以保证后面未处理的元素不会丢失

2. 边界处理的坑

步骤 1 里我们用 dest 来记录结果数组的下标,有可能出现 dest == n 的情况。
这种情况代表 最后一个位置正好是因为复写 0 产生的越界
处理办法是:

  • arr[n - 1] 强制置为 0
  • 然后 cur--, dest -= 2 回退到正确位置。

如果忘记处理这个边界,程序会 数组越界 or 少复写一个元素。这是这题的 最大坑

3. 为什么 dest 初始是 -1?

因为在第一次遇到非零元素时,dest 应该指向 0 号下标
如果初始化成 0,那么第一步就会偏移一位,导致整体错位。


六、复杂度分析

  • 时间复杂度:O(n)
  • 第一次扫描数组,找到最后一个需要复写的数,耗时 O(n);
  • 第二次从后往前复写,也最多 O(n);
  • 因此整体时间复杂度为 O(n)。
  • 空间复杂度:O(1)
  • 我们只使用了常数级别的额外变量 curdest
  • 属于 原地算法

七、总结

这道题与上一篇“移动零”非常相似,都是 双指针 的思路:

  • “移动零”从前往后,把非零数挪到前面;
  • “复写零”则是从后往前,把零多写一次。

两道题结合起来,能更好地理解:双指针既可以“前向扫描”,也可以“反向复写”

笔者的话:这道题如果不换角度,会卡在“覆盖问题”上。掌握了“从后往前复写”的技巧,你会发现很多数组操作题都能迎刃而解。

http://www.dtcms.com/a/434715.html

相关文章:

  • 英语学习-Saints039
  • 网站制作费用入什么科目淘宝客优惠券网站建设教程视频
  • CSS field-sizing 让表单「活」起来
  • 【Flutter】抽象类的运用(abstract与implements的实践)
  • 上海建设网站便宜的网站服务器是什么意思
  • 11.UE-游戏逆向-内存中的FUObjectArray(深入理解内存数据)
  • AI智能体在研究分析中的仿真应用:利他主义的悖论——是道德的顶峰,还是精致的利己?
  • SQL语句——高级字符串函数 / 正则表达式 / 子句
  • 西宁网站建设君博首选建设公司的网站首页
  • 【MySQL】数据库事务深度解析:从四大特性到隔离级别的实现逻辑
  • 2021 年真题配套词汇单词笔记(考研真相)
  • 儿童携带背包专利拆解:活动腿弹簧式伸缩卡扣与三模式(背包 / 座椅 / 安全椅)切换机制
  • 一般做一个网站专题页多少钱多商户商城源码下载
  • 利用网站建设平台河南省建设集团有限公司官网
  • **标题:发散创新:探索分布式账本的深度技术****摘要**:本文将深入探讨分布式账本技术,
  • Berachain稳定币使用指南:HONEY与跨链稳定币的协同之道
  • Ethernaut 1-10
  • 比特币、以太坊的“红与黑”:合法持有与传承的法律边界与警示
  • 函数的定义与使用
  • Citadel 发布首个原生支持 Sui 的硬件钱包 SuiBall
  • 游戏攻略新闻资讯主题模板源码 YK一点资讯模版 Zblog主题模版
  • 英语学习-Saints043-2
  • 手机网站html512306的网站是哪个公司做的
  • 认知篇#14:360度舵机和180度舵机的区别
  • 多模态大语言模型LISA
  • 第一次训练赛题解
  • 在线购物网站 模版怎么开网店具体流程
  • 返回值:on()方法返回的是当前jQuery对象
  • 你用过快捷支付吗?这是一种什么支付方式?
  • 自己做的网页加在网站文章上为什么打不开门户网站系统架构