题目解析
要求在保持原数组大小的情况下,将数组中的0复习,也就是改成两倍。然后非零的元素与0的相对位置保持不变,如果复写的数组已经到数组的边界就结束复写,就如示例1那样。
算法原理
使用双指针。
先用双指针异地模拟,再“就地”模拟,达到题目要求。
双指针创建:
cur:遍历数组
dest:指向已复写的最后一个元素
异地模拟
原
1 0 2 3 0 4 5 0
复写
1 0 0 2 3 0 0 4
原地模拟
从左往右复写会造成对原本数组的元素的覆盖
所以从右往左 或者说从后往前
cur :指向最后一个待复写的元素
dest : 指向数组的最后一个元素
可见从后往前复写就可以达到要求。
现在问题是如何找到最后一个需要复写的元素,也就是cur的起始位置该怎么找。
寻找最后一个“复写”元素位置
只要按照下面步骤,就能找到最后一个需要“复写”的位置。
结束时,cur指向的元素就是最后一个需要“复写”的位置
1.判断cur位置的值
2.决定dest往前走一步还是两步
3.判断dest是否到结束位置
4.cur++
处理边界情况
当最后一个复写元素是0时,dest连续走两步会直接到n(假设n为数组元素个数)位置,发生数组越界。
这时将数组的最后一个元素赋为0,然后cur向前移动一步,dest向前移动两步。
eg:
原 1 0 2 3 0 4
复写 1 0 0 2 3 0 (0)
这种情况下,最后一个0理论上dest是要进行复写的,但是已经发生了数组越界,leetcode上会直接报错。所以我们针对这种情况,先将这个0元素处理掉,然后让cur指向上一个需要处理的复写元素。
也就是说,这个情况下的cur指向的是 3 (原本应该是0)然后dest指向的是0
这是在找完最后一个复写元素时,两个指针的指向情况
这是在处理完边界后的双指针指向情况
关注不迷路~更多算法解析持续更新中~!
代码编写
c语言代码
void duplicateZeros(int* arr, int arrSize) {
//1.找到最后一个需要“复写”的数
int dest = -1;
int cur = 0;
while(cur < arrSize)
{
if(arr[cur] == 0)
{
dest+=2;
}
else
{
dest++;
}
if(dest >= arrSize-1)
break;
cur++;
}
//2.处理dest越界情况
if(dest == arrSize)
{
cur--;
arr[arrSize-1] = 0;
dest-=2;
}
//3.从后往前复写
while(cur>=0)
{
if(arr[cur] == 0)
{
arr[dest--] =0;
arr[dest--] = 0;
cur--;
}
else
{
arr[dest--] = arr[cur--];
}
}
}
C++代码
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
//1.找到最后一个需要“复写”的数
int dest = -1, cur = 0,n = arr.size();
while(cur < n)
{
if(arr[cur] )
{
dest++;
}
else
{
dest+=2;
}
if(dest >= n -1) break;
cur++;
}
//2.处理dest越界情况
if(dest ==n)
{
arr[n-1] = 0;
cur--;
dest-=2;
}
//3.从后往前复写
while(cur>=0)
{
if(arr[cur])
{
arr[dest--] = arr[cur--];
}
else
{
arr[dest--] =0;
arr[dest--] = 0;
cur--;
}
}
}
};