目录
在解决数组中的零移动到末尾的问题时,我们需要保持非零元素的顺序,并原地修改数组。以下是两种高效的解法及其详细分析。
方法一:两次遍历法
思路分析:
-
第一次遍历 :将所有非零元素移动到数组前端。使用指针
j
记录非零元素应插入的位置。遍历数组时,遇到非零元素就将其放到nums[j]
,然后j
递增。 -
第二次遍历 :将
j
之后的位置全部填充为零。
代码实现:
var moveZeroes = function(nums) {
let j = 0;
// 将非零元素移到前面
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== 0) {
nums[j] = nums[i];
j++;
}
}
// 剩余位置填零
for (let i = j; i < nums.length; i++) {
nums[i] = 0;
}
return nums;
};
示例解析:
-
输入
[0, 1, 0, 3, 12]
:-
第一次遍历后,非零元素
1, 3, 12
被移到前三位,j = 3
。 -
第二次遍历从索引3开始填零,结果为
[1, 3, 12, 0, 0]
。
-
复杂度:
-
时间复杂度:O(n),两次独立遍历。
-
空间复杂度:O(1),仅使用常量空间。
方法二:单次遍历交换法
思路分析:
-
使用双指针
i
(遍历数组)和j
(标记待插入位置)。当nums[i]
非零时,将其与nums[j]
交换,并递增j
。 -
此方法通过交换操作,逐步将非零元素移到前面,零被交换到后面。
代码实现:
var moveZeroes = function(nums) {
let j = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== 0) {
if (i !== j) { // 避免不必要的原地交换
[nums[j], nums[i]] = [nums[i], nums[j]];
}
j++;
}
}
return nums;
};
示例解析:
-
输入
[0, 1, 0, 3, 12]
:-
i=0
,元素为0,跳过。 -
i=1
,元素1,交换到j=0
,数组变为[1, 0, 0, 3, 12]
,j=1
。 -
i=3
,元素3,交换到j=1
,数组变为[1, 3, 0, 0, 12]
,j=2
。 -
i=4
,元素12,交换到j=2
,最终结果为[1, 3, 12, 0, 0]
。
-
复杂度:
-
时间复杂度:O(n),仅一次遍历。
-
空间复杂度:O(1),原地交换。
两种方法对比
方法 | 优点 | 缺点 |
---|---|---|
两次遍历法 | 逻辑简单,直接覆盖 | 需要额外填零的步骤 |
单次遍历交换法 | 一次遍历完成,更高效 | 交换操作可能稍慢 |
总结 :
两种方法均满足题目要求,选择依据具体场景。若数组零较多,两次遍历法可能更优;若零较少,交换法更高效。两种方法均保证了非零元素的顺序,且原地操作,符合题目要求。