携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
概述
数组算是一个并不复杂的数据结构,但就是这个,面试官就能在面试过程中考察出很多东西来。例如经典的排序问题,二分搜索,滑动窗口等等,都是在数组这种最基础等数据结构中处理问题的。
我们做数组类算法问题的时候,常常需要定义一个变量,明确该变量的意义,并不停的维护这个变量,也需要特别注意初始值
和边界
问题。
题目
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
输入: nums = [0]
输出: [0]
提示
1 <= nums.length <= 10^4
-2^31 <= nums[i] <= 2^31 - 1
代码实现
没接触过双指针的小伙伴,可能就会慢慢遍历,找到每一个为0
的元素,然后再去搜索查询出它后面不为0
的元素并交换两者的位置。
class Solution {
public void moveZeroes(int[] nums) {
for(int i = 0; i < nums.length; ++i){
// 需要将0移动到数组末尾,所以判断条件为当前元素 == 0
if(nums[i] == 0){
// 从当前元素后一位开始
int j = i + 1;
// 找到非0元素的位置
while(j < nums.length && nums[j] == 0){
++j;
}
// 防止越界
if(j != nums.length){
// 交换元素
nums[i] = nums[i] + nums[j] - (nums[j] = nums[i]);
}
}
}
}
}
上面的解法虽然能实现效果,但是可以看到耗时是非常的长的,因为在上面的代码中,有很多重复的步骤,每次找到为0
的元素之后都会去做一次后续元素的遍历,时间的开销都耗费在那上面了。我们可以通过双指针的方式,把每次遇到的非0
元素都移动到数组前半部分,这样可以避免重复的查询操作。
具体步骤:
- 定义
left
,right
两个指针指向数组开头; - 先移动
right
指针,去数组中查找到非0
的元素,将其移动到数组头部,也就是left
指针处; - 交换完成之后,将
left
指针后移一位; - 重复上面两个步骤,一直到遍历完整个数组中的元素位置。
class Solution {
public void moveZeroes(int[] nums) {
// 定义两个指针遍历,均先指向数组头部
int left = 0, right = 0, n = nums.length;
// 边界处理
while(right < n){
// 先移动right指针,找到非0的元素,将其与left指针交换位置,同时将left指针后移一位
if(nums[right] != 0){
nums[left] = nums[left] + nums[right] - (nums[right] = nums[left++]);
}
++right;
}
}
}