今天來作一道一樣簡單,可是挺有趣的題,題目來自leetcode189. 旋轉數組,題目描述以下:javascript
給定一個數組,將數組中的元素向右移動 k 個位置,其中 k 是非負數。java
示例 1:算法
輸入: [1,2,3,4,5,6,7] 和 k = 3 輸出: [5,6,7,1,2,3,4]解釋:
向右旋轉 1 步: [7,1,2,3,4,5,6]
向右旋轉 2 步: [6,7,1,2,3,4,5]
向右旋轉 3 步: [5,6,7,1,2,3,4]
示例 2:編程輸入: [-1,-100,3,99] 和 k = 2 輸出: [3,99,-1,-100]解釋:
向右旋轉 1 步: [99,-1,-100,3]
向右旋轉 2 步: [3,99,-1,-100]
說明:數組儘量想出更多的解決方案,至少有三種不一樣的方法能夠解決這個問題。
要求使用空間複雜度爲 O(1) 的 原地 算法。code
老規矩,在分析完題目後,我先來講說個人實現思路,再來解析優質的解答。blog
其實也不用被題目嚇到,說是旋轉數組,其實就是給定一個k表示要將數組最後一位數轉移到數組頭部次數的操做,好比第二個例子,k爲2,表示一共要進行兩次操做:ip
第一次將數組最後一位也就是99移動到數組頭部。此時數組變成了[99,-1,-100,3]
,接着第二次操做,這時候最後一位是3:leetcode
此時數組變成了[3,99,-1,-100]
。get
因爲k爲2,只須要執行2次,因此旋轉數組完成,思路已經很清晰了,咱們來實現它:
/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ var rotate = function(nums, k) { // 若是數組只有一位或爲空不用作任何操做 if(nums.length<=1){ return; }; while(k>0){ //每次取最後一位,並加入到頭部,因爲splice返回的是數組,利用拓展運算符...還原成單個元素 nums.unshift(...nums.splice(-1,1)); k--; }; };
哎,有同窗可能就想到了,我何須一個個的轉義,k爲2,說到底就是把數組倒數兩個元素整個搬到數組頭部便可,而後我就開始寫了以下代碼:
/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ var rotate = function (nums, k) { nums.length <= 1 ? nums : nums.push(...nums.splice(0, nums.length - k)); };
注意,這裏的splice我是正向剪切,緣由是我發現splice的第一個參數爲負數時,好比-1表示倒序最後一個開始,第二個參數無論是幾,都只能剪一個:
[1,2,3].splice(-1,1); //[3] [1,2,3].splice(-1,2); //[3]
前面的思路是把尾部剪切了拼到頭部,咱們何不反過來,好比[3,99,-1,-100]
咱們剪3,99
push到-100
後面呢,因此這纔有了nums.length - k
表示前面咱們應該剪切的個數。
很遺憾,這段代碼看似能夠,但提交掛掉了,緣由是這段代碼只知足數組length大於k的狀況,好比數組爲[1,2]
,k爲3,正確答案是交換3次變成[2,1]
;
而此時nums.length - k
爲-1,splice一大特色就是第二參數爲0或者負數,表示一個不剪切,因此不符合。
此時,博客園用戶love編程的小可愛想到了%
求餘,我立馬靈光閃現改進了代碼:
/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ var rotate = function (nums, k) { nums.length <= 1 ? nums : nums.push(...nums.splice(0, nums.length - (k % nums.length))); };
惟一區別只是在於nums.length - (k % nums.length)
,什麼意思呢?
好比有數組[1,2,3,4]
,k爲4,旋轉4次後你會發現結果和最初的樣子如出一轍。並且只要k爲數組長度的整數倍都會形成這種狀況。
因此好比k爲5,其實能夠當作4+1
次,咱們只用旋轉一次便可了,而%
求餘正好能達到這個效果:
8%4 //0 9%4 //1 3%4 //3 2%4 //2
因此用k%length算出咱們真正要旋轉數組的次數便可,一行代碼搞定。
題目要求最少三種方法解答問題,但我沒能想出其它作法,這裏再補充其它不錯的作法。
引用leetcode用戶秦時明月的一個不錯的作法,不用splice,直接利用pop便可:
/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ var rotate = function(nums,k) { for(var i = 0;i<k;i++){ nums.unshift(nums.pop()); }; };
這個就比我第一種實現要優雅的多了。
而後我在看leetcode用戶🎃的作法時,splice發揮到了極致:
/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ var rotate = function(nums, k) { nums.splice(0,0,...nums.splice(nums.length-k)) };
這代代碼執行是這樣,好比數組[1,2,3,4]
,假設k爲3,先執行...nums.splice(nums.length-k)
,獲得了2,3,4
。此時數組變成了[1]
。
接着執行前面的nums.splice(0,0,2,3,4)
,表示從0位前面插入元素2,3,4
因而變成了[2,3,4,1]
。
可是有個問題,數組爲[1,2],k爲5不知足(我也是寫到這裏才發現了...算了不刪除了,就當複習splice了)。
那麼關於本題就說到這了。