1. 题目解析
leetcode链接:https://leetcode.cn/problems/duplicate-zeros/
给你一个长度固定的整数数组 arr
,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。
示例 1:
输入: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]
2. 算法解析
先根据“异地”操作,然后优化成双指针下的“就地”操作
1. 异地操作:
cur
指向原数组,dest
指向新数组,他们的起始位置如下。
cur
当前指向的值为1,让dest++
,拷贝1,然后让cur++
。
- 此时
cur
位置值为0,让dest
向后移动两格,拷贝两次0,然后让cur++
。
- 以此类推,
cur
位置不为0,就拷贝1次,为0,就拷贝两次,直到dest
越界。
2. 优化为“就地”操作
cur
和dest
的初始位置如下。
cur
位置不为0,des
t就向后移动1格,为0就向后移动两格。此时肯定不能从左向右复写0,因为这会覆盖到有效数据2,只能先找到复写0后的最后一个位置,然后从右向左复写0。- 得到最后一个位置时,指针状态如下。
- 然后从右向左覆盖。
- 找最后一个位置时,有可能出现如下情况,
dest
越界。这种情况出现的原因,是因为cur
指向的最后一个位置是0。需要特殊处理。
- 相当于,此时
cur
位置的0只能复写一次,因为没有位置了。
3. 代码实现
class Solution {
public:
void duplicateZeros(vector<int>& arr)
{
// 1.先找到最后一个数
int dest = -1;
int cur = 0;
int n = arr.size();
while(cur < n)
{
if(arr[cur]) // 非零元素,dest+1
dest++;
else // 零元素,dest+2
dest+=2;
if(dest >= n - 1) break; // 如果dest超出数组范围,就break掉
cur++;
}
// 2.处理边界情况,0只复写一次
if(dest == n)
{
arr[dest - 1] = 0;
cur--;
dest-=2;
}
// 3.从后向前,完成复写操作
while(dest != cur)
{
if(arr[cur])
arr[dest--] = arr[cur--];
else
{
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
}
}
}
};