力扣283. 移动零:两种解法详解
题目描述
给定一个整数数组 nums
,要求将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。必须在不复制数组的情况下原地操作。
示例:
- 输入:
nums = [0,1,0,3,12]
→ 输出:[1,3,12,0,0]
- 输入:
nums = [0]
→ 输出:[0]
解法一:暴力循环法
思路
暴力法的核心思想是逐个遍历数组元素,当遇到零时,将其与后续的第一个非零元素交换,直到所有零被移动到末尾。
步骤
- 外层循环:遍历数组中的每个元素。
- 内层处理:若当前元素为零,找到它之后第一个非零元素并交换。
- 终止条件:当所有零都被移动到末尾时结束。
代码实现
public static void moveZeroes(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] == 0) {
int currentIndex = i;
for (int j = i; j < nums.length - 1; j++) {
if (nums[j + 1] == 0) continue; // 跳过零
// 交换零和非零元素
int temp = nums[j + 1];
nums[j + 1] = nums[currentIndex];
nums[currentIndex] = temp;
currentIndex = j + 1;
}
}
}
}
力扣通过截图
复杂度分析
- 时间复杂度:O(n²),每个零可能需要遍历剩余元素。
- 空间复杂度:O(1),原地操作。
解法二:双指针法(最优解)
思路
双指针法通过一次遍历完成操作。定义左指针 left
指向已处理序列的尾部,右指针 right
遍历数组。当 right
遇到非零元素时,将其与 left
指向的位置交换,并右移 left
。
步骤
- 初始化双指针:
left = 0
,right = 0
。 - 遍历数组:右指针不断右移。
- 当
nums[right] ≠ 0
时,交换nums[left]
和nums[right]
,然后left++
。
- 当
- 结果:所有非零元素被移动到数组前端,剩余位置自动填充零。
代码实现
public static void moveZeroes(int[] nums) {
int left = 0, right = 0;
while (right < nums.length) {
if (nums[right] != 0) {
// 交换并移动左指针
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
}
right++;
}
}
力扣通过截图
复杂度分析
- 时间复杂度:O(n),一次遍历即可完成。
- 空间复杂度:O(1),原地操作。
示例解析
以输入 nums = [0,1,0,3,12]
为例:
- 双指针法执行过程:
left=0
,right=0
:遇到零,right++
。right=1
(值为1):交换left=0
和right=1
,数组变为[1,0,0,3,12]
,left=1
。right=2
:值为零,跳过。right=3
(值为3):交换left=1
和right=3
,数组变为[1,3,0,0,12]
,left=2
。right=4
(值为12):交换left=2
和right=4
,得到[1,3,12,0,0]
,left=3
。
- 结果:所有非零元素保持顺序,零被移动到末尾。
总结
- 暴力法:思路直接但效率低,适合小规模数据。
- 双指针法:高效且简洁,通过一次遍历解决问题,是本题最优解。
相似问题:
掌握双指针法的思想,能有效解决数组操作中的许多问题!