Leecode 2024.3.15
4.移动零
给定一个数组 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
进阶:你能尽量减少完成的操作次数吗?
个人解法
思路:
不复制数组,那只能使用指针进行操作了。
所以就有了最简单的思路,建立两个指针left和right,left去遍历nums,而right一直在left之后去搜索:
当left指针遇到0时,让right指针去找在left右边的非0值,找到之后使用nums[left], nums[right] = nums[right], nums[left]去交换list数组的元素,把遇到的第一个非0值换到前面。
按照这个思路,left遍历完一遍数组,就能保证达到题目的要求。
from typing import List
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# 两个指针
if len(nums)>1:
for left in range(0, len(nums)-1):
if nums[left]==0:
for right in range(left+1, len(nums)):
if nums[right]!=0:
nums[left], nums[right] = nums[right], nums[left]
break
time:1586ms(6.87%)
mem:17.37MB(71.73%)
优化
过是过了,一看这个耗时,又要头疼了。
用到了两次循环一定会出现这个问题,现在我们来优化代码:
我们仔细回顾一下两个指针的作用和动作:
在上面的代码中,左边的指针需要遍历完全部nums,去找为0的元素;右边的指针在找非0元素。
这个逻辑是没有问题的,我们从另一个角度来想这个问题:我们要做的其实是把非0元素全部排在list前面。因此:
我们不需要管左边的left指针是不是0,right指针和left指针都从nums的起点出发,我们要做的事情就是,让快指针right遇到的非0值全部排到数组的前面,left指针只需要记录right指针遇到的非0值需要放的位置,出现一个非0值就交换一次,然后left右移一位即可。
class Solution:
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
left = 0 # 慢指针,left
for right in range(len(nums)): # 快指针,right,遍历数组
# 核心的交换步骤:如果当前right不为0,则交换到左侧,把非0数往左侧移动就对了
if nums[right]:
nums[left], nums[right] = nums[right], nums[left]
left += 1 # left被非0元素填占了后,挪开一个位置,放下一个数
time:46ms(69.17%)
mem:17.26MB(88.56%)
总结:
1.双指针:往往应用在直接修改数组的题目上,在编写程序时,需要思考清楚两个指针的动作逻辑,分析清楚即可;
2.nums[left], nums[right] = nums[right], nums[left]——这个语句可以同时交换list中的元素,不需要使用temp