LeetCode—283—Move Zeroes

weixin_33749242發表於2018-10-01

題目

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.

Example:

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

翻譯

給定一個陣列nums,寫一個函式,將陣列中所有的0元素,挪到陣列的末尾,而維持其他所有非0元素的相對位置。
舉例: [0,1,0,3,12],函式執行後結果為[1,3,12,0,0]

如何解決

(1)最直觀的思路
首先我們掃描一遍陣列,將所有非零元素全都拿出來。有幾個這樣的非零元素。就將這些元素填補到原先的陣列中。之後將沒填補的位置全都賦值為0;


13890024-021e7f1c5df8a0b5.png
1.png

取出所有非零元素


13890024-a76181aae21bf4e3.png
2.png

將非零元素填補到陣列中
13890024-129ad179c296647f.png
3.png

將沒填補的位置全都賦值為0
程式碼如下:

import java.util.*;

// 283. Move Zeroes
// https://leetcode.com/problems/move-zeroes/description/
// 時間複雜度: O(n)
// 空間複雜度: O(n)
class Solution1 {

    public void moveZeroes(int[] nums) {

        ArrayList<Integer> nonZeroElements = new ArrayList<Integer>();

        // 將vec中所有非0元素放入nonZeroElements中
        for (int i = 0; i < nums.length; i++)
            if (nums[i] != 0)
                nonZeroElements.add(nums[i]);

        // 將nonZeroElements中的所有元素依次放入到nums開始的位置
        for (int i = 0; i < nonZeroElements.size(); i++)
            nums[i] = nonZeroElements.get(i);

        // 將nums剩餘的位置放置為0
        for (int i = nonZeroElements.size(); i < nums.length; i++)
            nums[i] = 0;
    }
}

一級優化

上面這種解題方法雖然解決了問題。但是有沒有更好的解決方法呢,我們分析上面的這種解題思路,在上一個方法中我們新開闢了一片空間,用於儲存非零元素,所以他的空間複雜度為O(n),接下來這種解法,我們就用nums陣列本身來實現。
先看下面這張圖


13890024-bd93c2a96653de1f.png
5.png

在這裡我們新定義一個索引 k,在[0...k)這個前閉後開的區間中存放所有遍歷過的非0元素。我們開始遍歷陣列,當遍歷的元素是非零元素時,就將元素放在k所在的位置,同時為了存放下一個非0元素。k要加一,繼續遍歷陣列。


13890024-8c7c9c932a04ae6d.png
6.png

上圖中遍歷到第二個元素,為非0元素,所以要將1放到k所在的位置,此後k加一。


13890024-2440ac4692ce7564.png
7.png

到我們遍歷完陣列時,此時區間[0...k)存放了所有非零元素,下來我們要做的就是從當前k開始,將剩餘所有元素都賦值為0,從而完成整個操作,此時整個操作的空間複雜度為O(1)級別。
程式碼如下:

// 原地(in place)解決該問題
// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution2 {
    public void moveZeroes(int[] nums) {

        int k = 0; // nums中, [0...k)的元素均為非0元素

        // 遍歷到第i個元素後,保證[0...i]中所有非0元素
        // 都按照順序排列在[0...k)中
        for(int i = 0 ; i < nums.length ; i ++)
            if( nums[i] != 0 )
                nums[k++] = nums[i];

        // 將nums剩餘的位置放置為0
        for(int i = k ; i < nums.length ; i ++)
            nums[i] = 0;
    }
}

二級優化

通過前面的優化雖然我們在原地完成了move zeros的操作。但是當我們將所有的非0元素都挪到陣列的前面時。我們還要花費一些操作,將剩餘元素全都賦值為0。那麼顯而易見的想法是將所有非零元素放好位置時,相應的0元素已經放在了後面。其實我們可以通過將非0元素和0元素交換位置,從而達到這個目的。


13890024-d67272b7dbc12ab0.png
8.png

如上圖,此時遍歷到第四個元素,此時為非0元素。所以將第四個元素和k所在的元素交換位置,變成下面這幅圖,最終完成整個操作。


13890024-0bf93af3851ceb1c.png
9.png

程式碼如下:
// 原地(in place)解決該問題
// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution3 {
    public void moveZeroes(int[] nums) {

        int k = 0; // nums中, [0...k)的元素均為非0元素

        // 遍歷到第i個元素後,保證[0...i]中所有非0元素
        // 都按照順序排列在[0...k)中
        // 同時, [k...i] 為 0
        for(int i = 0 ; i < nums.length ; i ++)
            if(nums[i] != 0)
                swap(nums, k++, i);
    }

    private void swap(int[] nums, int i, int j){
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

最終優化

演算法的世界裡沒有最好,只有更好。在上面這個問題中,那麼有沒有一種可能就是nums陣列全是非零元素。那如果這樣的話。我們遍歷一遍,就相當於每個元素自己和自己交換了一遍位置。那麼這是我們不希望看到的。那麼在這裡我們還需要加入一個特殊的判斷。優化後的程式碼如下:

// 原地(in place)解決該問題
// 時間複雜度: O(n)
// 空間複雜度: O(1)
class Solution4 {

    public void moveZeroes(int[] nums) {

        int k = 0; // nums中, [0...k)的元素均為非0元素

        // 遍歷到第i個元素後,保證[0...i]中所有非0元素
        // 都按照順序排列在[0...k)中
        // 同時, [k...i] 為 0
        for(int i = 0 ; i < nums.length ; i ++)
            if(nums[i] != 0)
                if(k != i)
                    swap(nums, k++, i);
                else
                    k ++;
    }

    private void swap(int[] nums, int i, int j){
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

更多內容歡迎大家關注

13890024-ec220e0ec0e1b719
yuandatou

相關文章