leetcode刷題:283.Move Zeroes(Easy)

地址:https://leetcode.com/problems/move-zeroes/數組

應用場景說明

這個題是很Easy的一道題,它的應用場景是在我嘗試寫小遊戲2048時,採用了二維數組存放數字佔位,當按上下左右鍵時,要把全部的數字靠在一邊,而全部爲0的靠在另外一邊,這時候用到這個題的解題思路很快能作出來。數據結構

解法

目的就是把一個數組中全部爲0的數移動到數組的尾部,並保證其餘元素相對位置不變。要求是在原數組上修改,不要額外引入其餘的數組;儘可能減小操做次數。學習

輸入:優化

[0,1,0,3,12]

輸出設計

[1,3,12,0,0]

首先先引入額外數組的方式看如何來寫:code

var moveZeroes = function(nums) {
    let nonZeroArr = [];  // 用來存放非0元素
    for(let i = 0; i < nums.length; i++) {
      if(nums[i]){
        nonZeroArr.push(nums[i])
      }
    }
    // 把新數組中的每一項對位的賦值給原數組
    for(let j = 0; j < nonZeroArr.length; j++){
      nums[j] = nonZeroArr[j]
    }

    // 把nums中以後的位置都補上0
    for(let k = nonZeroArr.length; k < nums.length; k++){
      nums[k] = 0;
    }
    
    return nums;
};

let arr = [0,1,0,3,0,9,0,12];

console.log(moveZeroes(arr)); // [ 1, 3, 9, 12, 0, 0, 0, 0 ]

思路很簡單,就是把不爲0的數字存在一個新數組中,把新數組的每一項對位的從新賦值給原來數組的位置,而後把原數組剩餘的位置一概替換爲0;遊戲

這樣的寫法在時間複雜度上是 O(n) 級別的,在空間複雜度上也是O(n) 級別的,由於引入了新的數組。leetcode

能夠在不引入新數組的狀況下作位置的調整:get

var moveZeroes = function(nums) {
  let lastZeroIndex = 0;  // 每一次最後找到的0的位置,初始是0
  for(let i = 0; i < nums.length; i++){
    if(nums[i]) {  // 不爲0
      nums[lastZeroIndex++] = nums[i];  // 把遍歷到不爲0的數字,替換在0的位置,替換的位置已經不是0了,向後推一步;
    }
  }
  // 從k的位置開始以後的都應該爲0
  while(lastZeroIndex < nums.length){
    nums[lastZeroIndex++] = 0;
  }
  return nums;
};

let arr = [0,1,0,3,0,9,0,12];
console.log(moveZeroes(arr)); // [ 1, 3, 9, 12, 0, 0, 0, 0 ]

以上的作法只在一個數組中操做,用變量 repalceIndex 存一下要被非零數字替換的位置。在遍歷中遇到了非0數字,則替換在 repalceIndex的位置,再將repalceIndex向後推一位,等待下一次遇到非零數字來替換,等到遍歷結束,repalceIndex 實際上就是非零數字的個數,再從這個位置開始直到數組結束都替換爲0。io

這樣的寫法在時間複雜度上是 O(n) 級別的,在空間複雜度上也是O(1) 級別的,由於沒有引入了新的數組。

這樣還不是最終答案,由於在最後還須要一個 for 循環將剩下的位置都替換爲0,多了一步這樣的操做是不必的。

下面採用交換位置的方式。

var moveZeroes = function(nums) {
  let repalceIndex = 0;  // 記錄要被交換的位置
  let replaceElement; // 中間變量,用來存不爲0的數字
  for(let i = 0; i < nums.length; i++){
    if(nums[i]) {  // 當不爲0
      if(i != repalceIndex) {  // 第i個和repalceIndex相同,說明要交換的是同一個元素,不必進行交換操做
        replaceElement = nums[i];
        nums[i] = 0;
        nums[repalceIndex] = replaceElement;
      }
      repalceIndex++
    }
  }
  
  return nums;
};
let arr = [0,1,0,3,0,9,0,12];
console.log(moveZeroes(arr)); // [ 1, 3, 9, 12, 0, 0, 0, 0 ]

上面的思路是這樣的,repalceIndex 記錄了第一個是0的位置,當遇到非0的數字時候,就把非0的數字和repalceIndex所在位置的0交換位置,而後 repalceIndex 向前推動一位,繼續記錄的仍是第一個0的位置,遍歷中再與非0數字交換,再推動。。。重複這個過程直到遍歷結束。
判斷 i != repalceIndex 是一種優化手段,只有非0位置的數字和要交換的位置不是同一個位置才進行交換。

我本身在寫小遊戲2048的時候用到了這個題的解題思路。在小遊戲2048中,設置了和界面一致的二維數組,數組的每一位記錄了一個數字。

嘗試用在小遊戲2048中

我在寫2048小遊戲時,數據存放的是一個 4X4 的二維數組(固然有不少的作法,能夠採用別的方式),例如這樣:

let data = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]

這些0都是佔位的,表明的是還未填充任何數據,隨着玩家玩的過程,會出現不少數字分散在不一樣的位置上,當按上下左右時,總要把全部不爲0的數字撥在同一邊,0的數字在另外一邊,這時就用到上述這個題的解法便可。

let data = [
    [2, 0, 0, 0],
    [0, 4, 0, 0],
    [0, 0, 2, 4],
    [0, 2, 2, 0]
]
// 遍歷取出每個數組
for(let i = 0; i < data.length; i++){
    moveZeroes(data[i])
}

若是掌握了上述的技巧,不管是向左向右仍是向上向下均可以作到。

後記

在剛開始嘗試解這道題時,內心在想,出這道題的人是有多無聊,只是爲了創造問題而創造,壓根想不到這道題的應用場景在哪裏,這也跟本身的眼界有關係。當我在嘗試作2048小遊戲時,設計出來存放數據結構後,開始實現本身要實現的方式時,才意識到原來這麼簡單的一道題不是沒有場景,而是本身沒遇到,當遇到時豁然開朗。

這也給了我一個啓示,任何一道題存在必然是有緣由的,它就是解決了某一個問題而存在的,當我有了這樣的意識,在作每一道題的時候,會多問本身一下,這道題的應用場景在哪裏,我如今作的項目中有沒有相似的場景能夠套用,若是沒有,去找一種案例應用在場景中,這樣刷完提後不至於很快忘記,只要記住了案例,刷的題也天然而然的記住了。

以上若有誤差歡迎指正學習,謝謝。

相關文章
相關標籤/搜索