题目
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意,必须在不复制数组的情况下原地对数组进行操作。
思路
可以将这个数组划分为三块区域:
- 非零数字区域
- 全为 0 的区域
- 未处理区域
数组大小为 n,可以定义两个变量 cur = 0、dest = -1:
0 ~ dest表示非零区域dest + 1 ~ cur - 1表示零区域cur ~ n - 1表示未处理区域
由 cur 对数组进行遍历(每次都 ++)。当遇到的数是 0 时,零区域需要扩大,此时只执行 cur++;
若遇到的数非 0,则执行 dest++,此时非零区域变大。这时 dest 指向的是一个 0,而 cur 指向非 0,于是交换 nums[cur] 与 nums[dest]。交换后,dest 指向的就是非零元素,而 cur 指向 0。
参考代码
java
class Solution {
public void moveZeroes(int[] nums) {
int cur = 0, dest = -1;
while (cur < nums.length) {
if (nums[cur] != 0) {
dest++;
int tmp = nums[cur];
nums[cur] = nums[dest];
nums[dest] = tmp;
}
cur++;
}
}
}
题目
给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。请对输入的数组就地进行上述修改,不要从函数返回任何东西。
思路
从前往后开始复写 0,会导致被 0 覆盖的数字很难被记录下来,不如「正难则反」,从后往前开始复写 0。我们只需要记录原数组从前往后复写时,能复写到第几位数字就可以了。
定义两个变量:cur = 0、dest = -1。
0 ~ cur - 1表示已经统计好长度的原数组元素cur表示正在统计的原数组元素dest表示复写到哪一位n表示数组大小
先用 while (cur < n) 以 cur++ 的形式遍历数组:当 arr[cur] == 0 时,dest += 2,因为 0 要写两次,一次占用两个位置;否则 arr[cur] != 0 时,dest++。每次循环结束后 cur++。
同时要做特殊情况处理:在 dest 接近数组末尾时,dest 可能等于 n - 1,或者直接越界等于 n。此时对 dest == n 做特殊处理------因为 dest 能等于 n,是因为 cur 最后一次处理时 arr[cur] == 0,所以 dest += 2 后 dest == n。此时只需要令 arr[n - 1] = 0,再执行 cur--、dest -= 2,特殊情况即处理完成。
最后则是 while (cur >= 0) 从后往前循环:当 arr[cur] == 0 时,连续两次执行 arr[dest--] = 0,并执行 cur--;否则执行 arr[dest--] = arr[cur--]。
参考代码
java
class Solution {
public void duplicateZeros(int[] arr) {
int n = arr.length;
int cur = 0, dest = -1;
while (cur < n) {
if (arr[cur] == 0) {
dest += 2;
} else {
dest++;
}
if (dest >= n - 1) {
break;
}
cur++;
}
if (dest == n) {
arr[n - 1] = 0;
cur--;
dest -= 2;
}
while (cur >= 0) {
if (arr[cur] == 0) {
arr[dest--] = 0;
arr[dest--] = 0;
} else {
arr[dest--] = arr[cur];
}
cur--;
}
}
}