本题用双指针+模拟的方法来解
题目要求原地修改,我们可以先试着用异地双指针的方式,最后再优化为原地双指针。
异地双指针
先来写异地的:
当src不为0时,dest把src的值抄一下,然后src和dest同步++
当src为0时,dest抄一个0,然后++,再抄一个0:
然后dest和src同步++:
接下来就是接着走,最后走完是这个样子:当dest走到空就不再抄了:
现在试着合并为原地双指针:
原地双指针
src不为0,dest原地抄1,然后和src同步++。
此时src就指向0了,那么dest就要复写两个0:
问题来了,此时2已经被覆盖了,再想用2覆盖3就已经没有2了。
所以从前往后不可取,我们试着从后往前:
从后往前抄写
我们让src指向最后一个要抄的数:
此时src不为0,dest原地抄4:
然后dest和src同步--:
src为0,dest抄两个0:
然后就按部就班的走,走到最后是这个样子:
dest原地抄一遍1:
然后和src同步--,此时下标小于-1,结束:
那么接下来的重点就是我们怎么让src刚开始指向最后一个要抄的数?
双指针求最后一个要抄写的数
让dest指向-1的位置,src指向数组开头。
我们通过src是否为0来决定dest走一步还是走两步。
此时src不为0,那么dest走一步:
接下来就按部就班的走,走到最后是这个样子:
当dest走到数组的末尾时,src就不再走了,此时src指向的就是要抄写的最后一个数。
但是这里还是有一个特殊的情况:
边界情况
当数组是这样时候:
走到最后,会走成这个样子:
dest最后会走到越界的地方,所以这个越界情况需要处理一下。
处理边界情况
之所以出现这个越界情况是因为dest走到最后,src为0,dest要复写两个0,走两步造成的越界。
那我们就让它最后只复写一个0,仍然走两步走到空,但是不对越界的地址赋值0就不会报错。
code
int src=0;
int dest=-1;
int n=arr.size();
//寻找最后一个要抄写的数
while(src<n)
{
if(arr[src])dest++;
else dest+=2;
if(dest>=n-1)break; //>=n而不是=n的原因是有一个越界情况
src++;
}
//处理边界情况
if(dest==n)
{
arr[n-1]=0;
dest-=2;
src-=1;
}
//从后向前抄写
while(src>=0)
{
if(arr[src])
{
arr[dest--]=arr[src--];
}
else
{
arr[dest--]=0;
arr[dest--]=0;
src--;
}
}