返回

LeetCode 算法入门 — 移动零

前端

移动零:算法入门指南

踏入算法的世界,移动零 算法将作为你初学者之旅的垫脚石。看似简单,它却蕴含着算法思维的精髓,是算法入门必备的经典题型。

算法解析

原地操作法

想象一下你在操场玩耍,你的朋友都跑走了,只留下你一个人。为了重新加入游戏,你需要移动到一个非空的位置。同样的概念适用于移动零算法。

使用两个指针,一个指向当前元素,另一个指向非零元素的下一个位置。如果当前元素不为零,就像你是一个非零元素,那么你就和你的朋友(下一个非零元素)交换位置,然后你的朋友往前移动一步。如果当前元素为零,就像你是一个空位置,那么你往前移动一步,让非零元素填补你的位置。就这样,通过不断地交换和移动,所有的非零元素都聚集在数组的前面,而零元素被移动到数组的后面。

新建数组法

另一种方法是建立一个新的操场(数组),只邀请非零元素加入。遍历原操场,将所有非零元素带到新操场。然后,你只需销毁旧操场,将新操场作为你唯一的游乐场。

代码示例

def move_zeros(nums):
  """
  原地操作法移动零。

  参数:
    nums: 待移动零的列表。

  返回:
    无。
  """

  i = 0
  j = 0

  while i < len(nums):
    if nums[i] != 0:
      nums[i], nums[j] = nums[j], nums[i]
      j += 1
    i += 1


def move_zeros2(nums):
  """
  新建数组法移动零。

  参数:
    nums: 待移动零的列表。

  返回:
    无。
  """

  new_nums = []

  for num in nums:
    if num != 0:
      new_nums.append(num)

  nums[:] = new_nums

实例

考虑数组 [0, 1, 0, 3, 12]。

原地操作法:

当前位置 非零位置 交换 移动 数组
0 1 交换 [1, 0, 0, 3, 12]
1 2 前移 [1, 0, 0, 3, 12]
2 3 前移 [1, 0, 0, 3, 12]
3 4 交换 [1, 3, 0, 0, 12]
4 5 前移 [1, 3, 0, 0, 12]

新建数组法:

非零元素 新数组
1 [1]
3 [1, 3]
12 [1, 3, 12]

因此,最终数组为 [1, 3, 12, 0, 0]。

总结

移动零算法是算法入门旅程中一颗璀璨的宝石。它不仅教导了你算法思维,还为你的算法宝库增添了一项基本技能。当你踏上算法之旅时,记住这颗宝石,并用它来解锁算法世界中更多的奥秘。

常见问题解答

  1. 原地操作法和新建数组法哪个更好?

    两种方法各有优缺点。原地操作法效率更高,因为它只需要一次遍历。新建数组法更简单,因为它不需要复杂的指针操作。

  2. 移动零算法是否适用于负数?

    是的,移动零算法适用于负数和零。它会将所有非零元素移动到数组的前面,无论它们是正数还是负数。

  3. 移动零算法的时间复杂度是多少?

    原地操作法和新建数组法的最坏情况时间复杂度均为 O(n),其中 n 是数组的长度。

  4. 移动零算法的空间复杂度是多少?

    原地操作法的空间复杂度为 O(1),因为它不需要额外的空间。新建数组法的空间复杂度为 O(n),因为它需要一个新数组来存储非零元素。

  5. 移动零算法是否适用于链表?

    移动零算法可以应用于链表,但需要一些修改。链表中的指针操作略有不同,你需要遍历整个链表以找到非零元素。