2020年4月4日 星期六

Leetcode題解 Python: 四月挑戰DAY4 Move Zeroes

Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.

簡單來說,把數列中的零移到最後方,其他各數字的順序維持不變。

依舊很簡單,不過題目有要求:in-place。在數列直接修改,試著不用其他空間或是複製排好的數列。

如果用 for 且順序,那麼遇到0使用 append( pop() )會出錯。詳情不寫,改用倒敘法,這樣遇到 0 就能使用append( pop() )。

一次取出一次加到最後面,嫌太麻煩?不如用個 空串列 保存 pop() 出來的 0,最後再 extend() 保存 0 的串列即可恢復數列。


不過這樣沒有順著題目所期望的解法前進,用了額外的空間保存數列。

A two-pointer approach could be helpful here. The idea would be to have one pointer for iterating the array and another pointer that just works on the non-zero elements of the array.

這裡說用兩個指針,一個是迭代數列,另一個針對非0的元素。

針對迭代數列不用多心,要跑完數列自然就會用上。另外一個針對非0元素的要怎麼寫?

說到要針對非0元素,想必就要使用 if 去區分出來。遇到不是0的要怎麼處理?遇到0的又要怎麼處理?

如果要把 0 移動到最後方,路線有兩種:
1. 把0元素移動到最後方。
2. 把非0元素往前移動到最前方。

說到移動,就是把元素兩兩交換。交換就是一種 in-place 的作法,那要如何交換呢?

這裡使用 順序 去迭代數列,由前而後,所以被往前換的數字是不會再被迭代到而換到。

因此選擇把非0元素往前換,接下來只要不要動到換去前面的非0元素順序,最後就能把 0 都移動到最後方。

往前換,要往前多少?

從頭模擬起,如果一開始沒有碰到 0,就不需要換。接下來要是碰到0,也不用交換。在 0 之後又碰到非0元素,要往前換,要跟前面的0交換。

在遇到非0元素前,碰到一次0,跟前一格(0)換,碰到兩次0,跟前兩格的0交換。推理出,往前交換的位置跟碰到 0 次數是相同的。

碰到 0 ,指針 +1 。碰到 非0 ,就交換。132
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        zeroPointer = 0
        for i, num in enumerate(nums):
            if num == 0:
                zeroPointer += 1
            else:
                nums[i], nums[i-zeroPointer] = nums[i-zeroPointer], nums[i]
以下是我看其他大神的最貼切題目要求的寫法。
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        last_nnz = 0
        for i in range(len(nums)):
            if nums[i] != 0:
                nums[last_nnz], nums[i] = nums[i], nums[last_nnz] 
                last_nnz += 1