题目:复写零
虽然题目说必须要就地但是我们可以先试试异地然后再想办法优化成本地
算法讲解:
异地
可以定义两个指针让它们分别指向本地和异地,当本地指针指向零时这时候就往异地写入两个零其余就照常写,说完异地做法那我们应该如何优化成就地做法呢?
就地
本地也要定义两个指针
往前
cur往前每走一步时dest也会跟一步当cur指向0时dest会指向cur前面一个并且把它变成零那这样cur前面永远都是零了。
既然往前遍历不行那往后呢?
往后
要考虑 cur
和 dest
应该分别指向哪里。如果 cur
依然指向0,dest
指向-1,那和前向遍历是一样的,没有区别。但从题目中可以看到,输出结果的最后一个数字是4,而不是0。因此,我们可以将 cur
指向4的位置。cur
既然在前向遍历时指向第一个元素,那在后向遍历时指向输出结果的最后一个元素也是合理的。这样,cur
的位置已经确定了。那么 dest
应该指向哪里呢?
dest
是指向0还是5,或者其他位置?这个位置不好判断。不妨直接开始遍历。当 cur
指向4时,dest
应该复写4的位置;当 cur
指向0时,dest
应该将4和5都复写为0。这样我们可以推断出 dest
的位置。
找最后一个被复写数的位置
我们希望找到复写后数组的最后一个元素,但直接继续往后遍历显然不行。尽管通过题目我们知道最后一个元素的值,但是遍历的结束条件是什么呢?因此,我们需要改变策略:从往前遍历转为使用“快慢指针”方法。
快指针比慢指针走得更快。当快指针到达数组的末尾或空位时,慢指针应该指向正确的位置。这样我们就可以确保通过快慢指针的方法定位到数组复写后指定的最后一个元素。
但是,需要注意的是,这种方法也是有限制的。具体来说,当 cur
指向0时,dest
需要往前走两步,而在其他情况下,dest
只需要往前走一步。
边界问题
在力扣上的题目只要是野指针就会报错,那在上面我们说过“当快指针到达数组的末尾或空位时”其中只要指针指向空位就会报错,那该如何解决呢?
当 cur
指向0时,如果剩下的空间不足2个位置,会导致 dest
出现越界。这时我们可以直接将最后一个位置修改为0。这背后的原因是,如果空间不足两个位置,那最后一个位置必然是0,不然 dest
就不会越界。因此,我们可以放心地将其修改为0。
一旦最后一个位置被复写为0,意味着复写操作已经完成。这时,cur
应该向前移动一位,dest
也需要向前移动。不过,由于复写0需要占用两个位置,所以 dest
应该减去2。
代码实现:
class Solution
{
public:
void duplicateZeros(vector<int>& arr)
{
// 找到最后一个数
int len = arr.size();
int cur = 0;
int dest = -1;
while(cur < len)
{
if(arr[cur])
{
dest++;
}
else
{
dest+=2;
}
if(dest >= len - 1)
{
break;
}
cur++;
}
if(dest == len)
{
arr[len - 1] = 0;
dest-=2;
cur--;
}
//进行复写
while(cur >= 0)
{
if(arr[cur])
{
arr[dest--] = arr[cur--];
}
else
{
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
}
}
}
};