地址: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小遊戲時,數據存放的是一個 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小遊戲時,設計出來存放數據結構後,開始實現本身要實現的方式時,才意識到原來這麼簡單的一道題不是沒有場景,而是本身沒遇到,當遇到時豁然開朗。
這也給了我一個啓示,任何一道題存在必然是有緣由的,它就是解決了某一個問題而存在的,當我有了這樣的意識,在作每一道題的時候,會多問本身一下,這道題的應用場景在哪裏,我如今作的項目中有沒有相似的場景能夠套用,若是沒有,去找一種案例應用在場景中,這樣刷完提後不至於很快忘記,只要記住了案例,刷的題也天然而然的記住了。
以上若有誤差歡迎指正學習,謝謝。