代码:
class Solution {public void duplicateZeros(int[] arr) {int cur = 0, dest = -1, n = arr.length;// 1. 先找到最后⼀个需要复写的数while (cur < n) {if (arr[cur] == 0) dest += 2;else dest += 1;if (dest >= n - 1) break;cur++;}// 2. 处理⼀下边界情况if (dest == n) {arr[n - 1] = 0;cur--;dest -= 2;}// 3. 从后向前完成复写操作while (cur >= 0) {if (arr[cur] != 0) arr[dest--] = arr[cur--];else {arr[dest--] = 0;arr[dest--] = 0;cur--;}}}
}
题解:
我们可以定义两个指针,一个指针 cur 用来遍历数组,另一个指针 dest 用来复写元素到数组中
对于示例 1 如果我们采用从左到右的方式来复写元素:
cur cur
1 0 2 3 0 4 5 0
1 0 0
dest dest
当 cur 指向 1 时,将 dest 指向的位置设为 1,然后 cur 和 dest 都向右移,当 cur 指向 0 时,将 dest 指向的位置和下一个位置都设为 0 ,此时我们就会发现问题,源数组中的 2 还没有写就被覆盖了
所以采取从左到右的方式是错误的,我们应该使用从右到左的方式:
cur cur
1 0 2 3 0 4 5 0
0 0 4
dest dest dest
让 cur 指向最后一个复写的数,从右到左依次遍历复写,这样就不会出现覆盖的情况
那么现在我们面临的问题就是如何让 cur 去指向最后一个复写的数
我们采用以下的逻辑,模拟复写的过程,让 cur == 0,dest == -1,当 cur 指向的数不为 0 时,dest 就 ++,当cur 指向的数为 0 时 dest 就+= 2,直到 dest >= arr.length - 1 为止
cur cur cur
1 0 2 3 0 4 5 0
dest dest dest
我们通过上述流程便能让 cur 指向最后一个复写的数,并且 dest 刚好在数组的末尾,可以直接根据当前的位置进行从右到左的复写
我们要是只考虑上述的情况写出来的代码是有 bug 的
比如现在有一个例子: 1.0.2.0.3.0.5.0
我们正确复写的结果应该是 1,0,0.2.0.0.3.0
但通过我们上述的方法进行分析:
cur cur
1 0 2 0 3 0 5 0
1 0 0 2 0 0 3 0 0
dest dest
我们发现,当最后一个复写的数为 0 时,采用从右到左的方式复写可能会多出一个 0
所以我们要对这种特殊情况进行判断,当查找最后一个复写的数时 dest == arr.length ,就说明此时最后一个复写的数为 0,并且最终结果会多次一个 0 ,我们需要对特殊情况进行处理,将
arr[arr.length-1] = 0,cur-- ,dest -= 2