1. 程式人生 > >領釦網演算法學習筆記 -- 283

領釦網演算法學習筆記 -- 283

領釦網演算法學習筆記

本系列的演算法題目來自領釦網

陣列類演算法第一天

題目:

給定一個數組 nums,編寫一個函式將所有 0 移動到陣列的末尾,同時保持非零元素的相對順序。

示例:

輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]

說明:

  1. 必須在原陣列上操作,不能拷貝額外的陣列。
  2. 儘量減少操作次數。

解題過程:

思路一:

看到這題的第一個思路是:檢測到0後,將陣列後面的數往前移,往陣列的末尾補0,這樣就確保在原陣列上操作,且不用拷貝額外的陣列。

程式碼如下:

class Solution {
    public
void moveZeroes(int[] nums) { // 思路,從頭開始判斷是否是0,是的話,就將陣列後面的元素往前移一位,然後將0放置在最後。 int arraylength = nums.length; // 獲得陣列長度 int arrlength = arraylength; // 獲得迴圈次數 for(int i=0;i<arrlength;i++){ if(nums[i] == 0){ // 判斷當前值是否為0; for
(int j=i;j<arraylength-1;j++){ //將剩餘部分的資料往前移一位 nums[j]=nums[j+1]; } nums[arraylength-1] = 0; // 將末位置0 i--; arrlength--; } } } }

後續思考:

該程式碼在領釦上用時37ms

這樣做的話,陣列是隻用遍歷一次,但是陣列沒出現一個0,移動數組裡的元素的代價(次數)就很大,於是就思考能不能不移動,

思路二:

當出現0時我不移動,只是繼續往後讀,當往後讀遇到不是0的時候,將資料移到是0的位置,

於是我定義一個變數用來記錄0所在的位置,但是又遇到一個新問題,這個變數怎麼賦初值(就是起始0在哪),

這個問題其實很多餘,遍歷的資料不同,起始的0的位置也不同,沒辦法固定,所以我直接遍歷,當出現第一個0 的時候就設定這個值。然後繼續往下遍歷,不是0就與當前位置交換,當陣列遍歷完後,在之前的陣列長度與目前記錄0的位置的值的差值就是要補的0的個數,於是再補0就好。

程式碼如下:

class Solution {
    public void moveZeroes(int[] nums) {
        // 定義一個變數記錄當前為0的位置,然後查詢下一個資料是否為0,當資料不為0時,與當前資料互換位置
        int i = 0;                           // 獲取一個迴圈變數,記錄當前迴圈到哪了
        int locationnum = 0;                     // 暫時定義0位為第一個為0的位置
        int arraylength = nums.length;              // 獲得陣列長度
        for(;i<arraylength;i++){                 // 先讀取第一個0的位置
            if(nums[i]==0){
               locationnum = i;
               break;
            }
        }
        for(i++;i<arraylength;i++){                 // 判斷接下來的數是否為0
            if(nums[i]!=0){                     // 如果不為0,則與當前的0的位置交換
                nums[locationnum] = nums[i];        // 然後記錄0的位置往前移一位
                locationnum++;
            }
        }
        if(locationnum != 0){
        	for(;locationnum<arraylength;locationnum++){  // 將不是0的數全部往前挪了,剩下的就全是0
            	nums[locationnum] = 0;
        	}
        }
    }
}
// 用時2ms

領釦上面該題其他高質量範例:

class Solution {
    public void moveZeroes(int[] nums) {
		int j = 0 ;
		for(int i=0;i<nums.length;i++){
			if(nums[i]!=0){
				int temp = nums[i];
				nums[i] = nums[j];
				nums[j++] = temp;
			}
		}
    }
}
// 用時1ms

自我整理:這部分程式碼精簡,我看第一遍直接暈了,第二遍才反應過來,思路是這樣的:

  1. 定義一個變數 j 用來迴圈賦值

  2. 迴圈陣列

  3. 判斷當前迴圈值是不是為0,不為0,就將當前值與迴圈變數位置的值進行交換,然後將迴圈變數往前移一位

  4. 如果是0,則跳過

    這程式碼比我的優在不用去找第一個0 在哪,直接迴圈就OK了,

    但是我覺得,如果一個較大的陣列,裡面0的數量較少,那麼這個演算法我感覺就會時間變長,不能算是最優的,因為沒一個不為0 的數,它都要執行額外的三條語句。畢竟剛入門演算法,它的複雜度我暫時還不會算,所以只是個人猜想。

    class Solution {
        public void moveZeroes(int[] nums) {
            int idx = 0;
    		for (int num : nums) {
    			if (num != 0) {
    				nums[idx++] = num;
    			}
    		}
    		while (idx < nums.length) {
    			nums[idx++] = 0;
    		}
        }
    }
    // 用時2ms
    

    自我整理:這個程式碼比我的要簡潔,我擦,看樣子我要優化我的程式碼了,這個思路很清晰:

    1. 定義一個變數用來記錄非 0 的數量

    2. 將不是0的資料儲存出來(這裡只是讀取不是0的變數,然後一直儲存,不是像我之前那樣後面的資料全部往前移)

    3. 將0補齊

class Solution {
    public void moveZeroes(int[] nums) {
        int count = 0;
        int nonZeroIndex = 0;
        for(int i = 0;i<nums.length;i++)
        {
            if(nums[i] == 0)
            {
                count++;
            }
            else{
                nums[nonZeroIndex++] = nums[i];
            }
        }
        for(int i = nums.length-count;i<nums.length;i++)
        {
            nums[i] = 0;
        }
    }
}
// 用時3ms

自我整理:這個演算法思路也很清晰:

  1. 定義兩個變數,一個記錄0的個數,一個記錄當前資料已經儲存到那個位置了

  2. 然後迴圈陣列,遇到0,記錄 0 的個數就加一,不是0,就儲存資料,記錄當前儲存位置的變數就+1

  3. 後面就再補齊0

自我總結:演算法我覺得精簡是一條優化的道路,但是精簡的程式碼不代表執行效率就高,但是我目前很明顯的缺點是:邏輯是怎樣,程式碼就怎樣寫,導致程式碼過長,這一點反應了我的邏輯思維還不夠好,還需要鍛鍊。