双指针2:1089. 复写零
链接:1089. 复写零 - 力扣(LeetCode)
双指针的基本思想:
首先根据异地操作确定指针的基本步骤,再将异地操作优化成原地操作的双指针解法
题解:
异地操作:
fast指针指向原数组,slow指向新数组。fast指针遍历原数组,如果fast遇到0,slow指针位置和其下一位置都为0,slow+=2;如果fast遇到非0,slow指针位置拷贝该非0元素,slow+=1。当slow超过数组大小时停止。
优化后的双指针本地操作:
fast和slow初始都为0,fast指针遍历数组,如果fast遇到0,slow位置及其下一位置都为0,slow+=2。但是很明显这样是行不通的,因为slow连续两个位置都是0,会覆盖还没有被fast遍历的元素,因此必须另辟蹊径。
逆序进行双指针操作,但是由于不能在超过数组长度的位置复写,所以要先确定最后一个复写的数。首先使用双指针正向遍历数组,不做任何操作,仅仅确定最后一个复写数的位置,让fast遍历数组,遇到0,slow+=2,遇到非0.slow+=1,当slow到达n-1位置时停止,不做复写操作。此时fast和slow都确定好了位置,从此处开始逆向复写数组,解决问题。
(注意:使用双指针正向遍历数组确定最后一个复写数位置时,当最后一个复写数为0时,slow会越界,因此必须进行额外处理,如果最后一个复写数为0,在逆序复写时,只将slow-1的位置复写为0,因为slow位置越界了)
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
//确定逆序开始的位置
int fast = 0, slow = -1;
int n = arr.size();
for (; fast < n; fast++)
{
if (arr[fast] == 0) slow += 2;
else slow += 1;
if (slow >= n-1) break;
}
//处理边界条件
if (slow == n)
{
arr[slow - 1] = 0;
slow -= 2;
fast -= 1;
}
//逆序复写0
for (; slow >= 0; fast--)
{
if (arr[fast] != 0)
{
arr[slow] = arr[fast];
slow -= 1;
}
else
{
arr[slow] = arr[slow - 1] = 0;
slow -= 2;
}
}
}
};