算法2,复写零

1089. 复写零 - 解题思路整理

题目分析

问题描述

给定一个固定长度的整数数组 arr,对数组中的每个 0 进行复写(每个 0 在数组中占据两个位置),并将其余元素向右平移。如果数组长度不足,则舍弃超出长度的元素

核心要求

  1. 对数组中的 0 进行复写(每个 0 变成两个 0)

  2. 非 0 元素保持原值

  3. 修改必须在原数组中进行

  4. 从数组末尾开始处理,避免覆盖未处理的元素

示例说明

复制代码
示例1:输入: [1,0,2,3,0,4,5,0]
        输出: [1,0,0,2,3,0,0,4]
        
示例2:输入: [0,1,2,3,0,4]
        输出: [0,0,1,2,3,0]
        特殊情况:最后一个0被舍弃,因为数组长度不足

算法分析

1. 问题难点

  • 从前往后复写会覆盖还未处理的元素

  • 需要先知道哪些元素会被保留,哪些会被舍弃

  • 必须从后往前进行复写操作

2. 双指针法(从后向前)

算法思想

  1. 第一阶段:模拟从前往后复写的过程,找到最后一个会被保留的元素

  2. 第二阶段:从后往前进行实际的复写操作

指针定义

  • cur:从前往后扫描指针,用于模拟复写过程

  • dest:从前往后复写时的目标位置指针

  • 在第二阶段,curdest的含义会变化


第一阶段:寻找最后一个复写的数

目标:确定在复写后,数组中最后一个元素是什么,以及它是从哪里复写过来的。

初始化

  • cur = 0:当前处理的元素位置

  • dest = -1:如果在这里复写,应该写入的位置

处理逻辑

复制代码
while (cur < arr.length) {
    if (arr[cur] != 0) {
        // 非0元素,dest只移动一步
        dest++;
    } else {
        // 0元素,dest需要移动两步(复写)
        dest += 2;
    }
    
    // 判断dest是否已到达或超过数组末尾
    if (dest >= arr.length - 1) {
        break;  // 找到最后一个复写的元素
    }
    cur++;
}

特殊情况处理

复制代码
// 如果dest正好等于arr.length(越界1位)
if (dest == arr.length) {
    // 说明最后一个位置需要放置一个0的复写
    arr[arr.length - 1] = 0;
    dest -= 2;  // dest回退两位
    cur--;      // cur回退一位
}

执行过程示例

复制代码
输入: [1,0,2,3,0,4,5,0]  (长度8)

步骤    cur    dest     arr[cur]    操作
0)      0      -1       1           dest=0
1)      1       0       0           dest=2
2)      2       2       2           dest=3
3)      3       3       3           dest=4
4)      4       4       0           dest=6
5)      5       6       4           dest=7
6)      6       7       5           dest=8 (停止)

此时:
- dest = 8 (已越界,需要特殊处理)
- cur = 6 (指向元素5)
- 最后一个被处理的元素是5

第二阶段:从后往前复写

目标:从后向前遍历数组,将元素复写到正确位置。

处理逻辑

复制代码
// 此时cur指向最后一个被处理的元素
// dest指向最后一个应该写入的位置

while (cur >= 0 && dest >= 0) {
    if (arr[cur] != 0) {
        // 非0元素,直接复制
        arr[dest] = arr[cur];
        dest--;
    } else {
        // 0元素,需要复写两次
        if (dest >= 1) {
            arr[dest] = 0;
            arr[dest - 1] = 0;
        } else if (dest == 0) {
            // dest=0时,只能写入一个0
            arr[dest] = 0;
        }
        dest -= 2;
    }
    cur--;
}

示例执行(继续上面的例子):

复制代码
第一阶段结束后:
cur = 6, dest = 8 (越界,需调整)
arr = [1,0,2,3,0,4,5,0]

特殊情况处理:
dest == 8 > 7,需要特殊处理
arr[7] = 0  (最后一个位置放0)
dest = 8-2 = 6
cur = 6-1 = 5

从后往前复写:
cur=5, dest=6: arr[5]=4 → arr[6]=4
cur=4, dest=5: arr[4]=0 → arr[5]=0, arr[4]=0
cur=3, dest=3: arr[3]=3 → arr[3]=3
cur=2, dest=2: arr[2]=2 → arr[2]=2
cur=1, dest=1: arr[1]=0 → arr[1]=0, arr[0]=0
cur=0, dest=-1: 结束

最终结果: [0,0,1,2,3,0,0,4]

完整算法步骤总结

步骤1:寻找最后一个复写的数

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

  2. 遍历数组:

    • 如果 arr[cur] == 0dest += 2

    • 否则,dest += 1

    • dest >= arr.length-1时停止

  3. 处理边界情况:

    • 如果 dest == arr.length,在末尾放一个0,dest-=2, cur-=1

步骤2:从后往前复写

  1. cur开始向前遍历:

    • 如果 arr[cur] != 0arr[dest--] = arr[cur]

    • 如果 arr[cur] == 0

      • arr[dest--] = 0

      • 如果 dest >= 0arr[dest--] = 0

  2. 继续直到 cur < 0


时间复杂度与空间复杂度

时间复杂度:O(n)

  • 第一阶段:遍历一次数组,找到最后一个元素

  • 第二阶段:反向遍历一次数组

  • 总共两次遍历,O(2n) = O(n)

空间复杂度:O(1)

  • 只使用了常数个额外变量

  • 满足原地操作的要求


关键技巧总结

  1. 双向指针:一个指针模拟复写过程,一个指针记录目标位置

  2. 从后往前处理:避免覆盖未处理的元素

  3. 边界处理:特别注意数组末尾的0复写可能导致越界

  4. 分阶段处理:先"模拟"找到边界,再实际执行

这种方法的核心思想是先虚拟地走一遍复写过程,确定哪些元素会被保留,然后从后往前进行实际复写,确保不会覆盖还未处理的元

复制代码
class Solution {
    public void duplicateZeros(int[] arr) {
        //先找到最后一个复写的数字
        int cur=0;
        int dest=-1;
        for(cur=0;cur<arr.length;cur++){
            if(arr[cur]!=0){
                dest++;
            }else{
                dest+=2;
            }
            if(dest>=arr.length-1)
            break;
        }
        //特殊情况
        if(dest==arr.length){
            arr[arr.length-1]=0;
            cur--;
            dest-=2;
        }
        //从后往前完成复写操作
        while(cur>=0){
            if(arr[cur]!=0){
                arr[dest--]=arr[cur--];
            }else{
            arr[dest--]=0;
            arr[dest--]=0;
            cur--;
            }
        }
    }
}

素。

相关推荐
袋鼠云数栈2 小时前
集团数字化统战实战:统一数据门户与全业态监管体系构建
大数据·数据结构·人工智能·多模态
小月球~3 小时前
天梯赛 · 并查集
数据结构·算法
仍然.3 小时前
算法题目---模拟
java·javascript·算法
三道渊5 小时前
C语言:文件I/O
c语言·开发语言·数据结构·c++
kali-Myon5 小时前
CTFshow-Pwn142-Off-by-One(堆块重叠)
c语言·数据结构·安全·gdb·pwn·ctf·
潇冉沐晴5 小时前
DP——背包DP
算法·背包dp
GIOTTO情6 小时前
2026 世界互联网大会亚太峰会|AI 时代媒介投放的技术实战与算法优化
人工智能·算法
逆境不可逃6 小时前
LeetCode 热题 100 之 543. 二叉树的直径 102. 二叉树的层序遍历 108. 将有序数组转换为二叉搜索树 98. 验证二叉搜索树
算法·leetcode·职场和发展
计算机安禾6 小时前
【数据结构与算法】第19篇:树与二叉树的基础概念
c语言·开发语言·数据结构·c++·算法·visual studio code·visual studio
副露のmagic6 小时前
哈希章节 leetcode 思路&实现
算法·leetcode·哈希算法