题目描述
简单
提示
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
提示:
1 <= nums.length <= 104-231 <= nums[i] <= 231 - 1
进阶: 你能尽量减少完成的操作次数吗?
思路1 记录出现的0的数量
本题需要保证非0元素的相对次序,因此不能仅仅简单的将0交换到末尾部分。
思路描述:记录之前出现的0的数量X,当遇到一个非0元素时,将这个元素向前移动X个位置(最终位置)。
思路描述图
从图示中不难看出:zeroNums实际上记录非0元素最终应该放置的位置偏移量,比如上面数组中第一个元素是0,zeroNums=1,这就代表后面的非0元素需要向前移动一个位置以覆盖这个0。当遍历到第三个元素(0)时,zeroNums=2,这就代表后面的非0元素需要向前移动两个位置以覆盖之前出现的2个0导致的空余位置。最后所有非0元素都被移动到数组前半部分,后面的部分都需要置为0。
func moveZeroes(nums []int) {
// 从前往后遍历,记录出现的0的数量,非0元素往前移动的步数取决于其值
zeroNums := 0
for cur := 0; cur < len(nums); cur++ {
if nums[cur] == 0 {
zeroNums++
continue
}
nums[cur-zeroNums] = nums[cur]
}
for i := len(nums)-1; i >= len(nums)-zeroNums; i-- {
nums[i] = 0
}
}
思路2:直接将数组当作容器
思路描述:记录存放下一个非0元素的下标(初始为0),遇到非0元素就将其放置到最终位置并更新记录下标,最终将下标最终位置一直到数组末尾的元素全部置为0即可。
func moveZeroes(nums []int) {
next := 0
for i := 0; i < len(nums); i++ {
if nums[i] != 0 {
nums[next] = nums[i]
next++
}
}
clear(nums[next:])
}
思路3:双指针
思路描述:使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。右指针不断向右移动,每次右指针指向非0数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
- 左指针左边均非0
- 右指针左边指直到左指针处(范围区间)均为0
因此每次交换,都是将左指针的0与右指针的非0数交换,且非0的相对顺序并未改变。
func moveZeroes(nums []int) {
left, right, n := 0, 0, len(nums)
for right < n {
if nums[right] != 0 {
nums[left], nums[right] = nums[right], nums[left]
left++
}
right++
}
}