JS leetcode 旋轉數組 題解分析

壹 ❀ 引

今天來作一道一樣簡單,可是挺有趣的題,題目來自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,99push到-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了)。

那麼關於本題就說到這了。

相關文章
相關標籤/搜索