持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情
题目链接:1089. 复写零
题目描述
给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。
要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。
提示:
示例 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,题目限制条件为 就地修改。
需要注意的是,需要保持数组长度不变,也就是数组后面溢出的元素需要删除。
解题思路分析
首先观察题目数据范围,数组长度在 以内,暴力双层循环解决的时间复杂度为 会超时 TLE。
又因为题目有限制条件 “就地修改”,也就是不能使用额外的空间进行存储,如果没有限制条件的情况下,我们可以开辟另一个数组或者栈进行存储,然后进行模拟即可。
我们考虑数组未被删除部分中每有一个 0 时,数组末尾就会有一个元素被删除,我们可以遍历一遍数组,统计末尾需要删除的元素个数,以及复写 0 后的数组末尾位置在哪。
核心思路:在于找到复写 0 后的数组中最后一个元素在原数组的什么位置,然后对数组从后往前依次模拟放置元素即可。
具体实现
i指针从前往后遍历,每当遇到一个0时就让j指针从后往前移动一个位置,表示删除元素个数。- 利用
k指针来记录从后往前模拟放置元素的开始位置,初始化为n - 1,如果修改后的数组最后一位为0时,并且当前不为复写的那一个0,是原数组中的0,那么在从后往前模拟放置的时候,仅需放置一个0不用复写这个0,所以特殊处理这种情况。 - 从后往前模拟放置元素,遇到
0复写即可。
在实现过程中特别需要 注意 的是边界情况,也就是复写
0后的数组中最后一个元素为单个0的情况。
复杂度分析
- 时间复杂度:,其中
n为数组arr的长度。需要遍历两遍数组。 - 空间复杂度:,仅使用常量空间。
代码实现
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,采取特殊处理。 - 测试结果:
结束语
热爱是生活强有力的助推器,它会赋予你能量,促使你不断探寻生命的更多可能性。心有所爱,眼中自有光芒。去找到你所热爱的事,并为之全力以赴,活成自己喜欢的样子。