返回

复写零:从简单到深刻的算法思维

后端

复写零:表面上的简单

LeetCode 1089 问题如下:

给定一个数组 nums,其中每个元素都是正整数,请将所有 0 移到数组的末尾,同时保持非零元素的相对顺序。

例如:

输入:nums = [1,0,2,0,3,0,4,5]
输出:[1,2,3,4,5,0,0,0]

乍一看,这是一个非常简单的题目。我们可以使用一个简单的循环,逐个检查数组中的每个元素,并将非零元素移动到数组的前面,同时将零元素移动到数组的末尾。然而,这种朴素算法的时间复杂度为 O(n^2),其中 n 是数组的长度。对于大型数组,这种算法的效率会非常低。

优化算法:从朴素到高效

为了提高算法的效率,我们可以使用双指针法。我们使用两个指针 ij,其中 i 指向非零元素,j 指向零元素。然后,我们执行以下步骤:

  1. 如果 nums[i] 非零,则将 nums[i]nums[j] 交换,然后将 ij 都向右移动一步。
  2. 如果 nums[i] 为零,则仅将 j 向右移动一步。

通过这种方法,我们只遍历数组一遍,时间复杂度降为 O(n)。

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

数论的妙用:一次性解决问题

除了双指针法,我们还可以使用数论来一次性解决这个问题。我们可以利用数组 nums 中非零元素的个数 cnt。然后,我们从后往前遍历数组,对于每个非零元素 nums[i], 将 nums[cnt + i] 设置为 nums[i]。最后,将剩余的元素全部设置为 0。

def duplicateZeros(nums):
    cnt = 0
    for num in nums:
        if num != 0:
            cnt += 1

    i = cnt - 1
    j = len(nums) - 1
    while i >= 0:
        if nums[i] != 0:
            nums[j] = nums[i]
            j -= 1
        if i > 0 and nums[i] == 0:
            nums[j] = 0
            j -= 1
        i -= 1

结语

复写零问题看似简单,但它展示了从朴素算法到优化算法,再到数论应用的算法思维进化过程。通过剖析不同算法的优缺点,我们加深了对算法思维的理解,也提高了解决实际问题的效率。持续探索算法的奥秘,解锁更广阔的编程世界。