【C/C++】1089. 复写零

95 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情


题目链接:1089. 复写零

题目描述

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

注意:请不要在超过该数组长度的位置写入元素。

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

提示:

  • 1arr.length100001 \leqslant arr.length \leqslant 10000
  • 0arr[i]90 \leqslant arr[i] \leqslant 9

示例 1:

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

示例 2:

输入: [1,2,3]
输出: null
解释: 调用函数后,输入的数组将被修改为:[1,2,3]

整理题意

题目给定一个整数数组 arr,将数组中为 0 的整数复写一遍在其后面,需要注意的是后面的所有元素需要往后移动一个位置,也就是在 0 和其后面一个元素中间插入一个 0,题目限制条件为 就地修改

需要注意的是,需要保持数组长度不变,也就是数组后面溢出的元素需要删除。

解题思路分析

首先观察题目数据范围,数组长度在 10410^4 以内,暴力双层循环解决的时间复杂度为 O(n2)O(n^2) 会超时 TLE

又因为题目有限制条件 “就地修改”,也就是不能使用额外的空间进行存储,如果没有限制条件的情况下,我们可以开辟另一个数组或者栈进行存储,然后进行模拟即可。

我们考虑数组未被删除部分中每有一个 0 时,数组末尾就会有一个元素被删除,我们可以遍历一遍数组,统计末尾需要删除的元素个数,以及复写 0 后的数组末尾位置在哪。

核心思路:在于找到复写 0 后的数组中最后一个元素在原数组的什么位置,然后对数组从后往前依次模拟放置元素即可。

具体实现

  1. i 指针从前往后遍历,每当遇到一个 0 时就让 j 指针从后往前移动一个位置,表示删除元素个数。
  2. 利用 k 指针来记录从后往前模拟放置元素的开始位置,初始化为 n - 1,如果修改后的数组最后一位为 0 时,并且当前不为复写的那一个 0,是原数组中的 0,那么在从后往前模拟放置的时候,仅需放置一个 0 不用复写这个 0,所以特殊处理这种情况。
  3. 从后往前模拟放置元素,遇到 0 复写即可。

在实现过程中特别需要 注意 的是边界情况,也就是复写 0 后的数组中最后一个元素为单个 0 的情况。

复杂度分析

  • 时间复杂度:O(n)O(n),其中 n 为数组 arr 的长度。需要遍历两遍数组。
  • 空间复杂度:O(1)O(1),仅使用常量空间。

代码实现

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int n = arr.size();
        //i表示留下来的整数,j表示即将删除的整数,k记录最后一位是否为0
        int i = 0, j = n - 1, k = n - 1;
        while(i <= j){
            if(arr[i] == 0){
                //特判最后一个为0时的情况
                if(i == j){
                    //已判无需删除
                    arr[n - 1] = 0;
                    //k记录最后一位为0时的开始模拟位置
                    k = n - 2;
                    j--;
                    break;
                }
                j--;
            }
            i++;
        }
        //此时j的位置为数组复写0后能够保留的最后一位整数位置
        while(j >= 0){
            //遇到0复写即可
            if(arr[j] == 0){
                arr[k] = 0;
                k--;
            }
            arr[k] = arr[j];
            k--;
            j--;
        }
    }
};

总结

  • 该题如果在比赛中则需要首先考虑编码速度以及是否能够通过,无需考虑空间复杂度等问题限制,可直接利用额外数组进行解题。
  • 如果在面试中则需要考虑时间和空间的复杂度问题,则需要仔细考虑优化问题,所以双指针为该题的正确解法。
  • 该题中需要注意的是边界问题,需要考虑复写后最后一个 0 是原数组中的 0 还是复写的 0,采取特殊处理。
  • 测试结果:

微信截图_20220617151014.png

结束语

热爱是生活强有力的助推器,它会赋予你能量,促使你不断探寻生命的更多可能性。心有所爱,眼中自有光芒。去找到你所热爱的事,并为之全力以赴,活成自己喜欢的样子。