力扣之反轉字串之原地修改輸入陣列(雙指標方式)

水冗水孚發表於2023-01-27

題目描述

編寫一個函式,其作用是將輸入的字串反轉過來。輸入字串以字元陣列 s 的形式給出。

不要給另外的陣列分配額外的空間,你必須原地修改輸入陣列、使用 O(1) 的額外空間解決這一問題。

示例 1:

輸入: s = ["h","e","l","l","o"]
輸出: ["o","l","l","e","h"]

示例 2:

輸入: s = ["H","a","n","n","a","h"]
輸出: ["h","a","n","n","a","H"]

提示:

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 碼錶中的可列印字元
力扣原題目地址:https://leetcode.cn/problems/...

思路解法

分析

大多數人,看到這個題目以後,心想:呵!直接呼叫陣列的reverse()方法不就行啦。其實這的確是一種解決方案。只不過題目要求:原地修改輸入陣列、使用 O(1)  的額外空間。也就是說,要儘可能使用較小的記憶體去操作,這也是效能最佳化的一種嘗試。

我們想一下,其實陣列的reverse()方法的最終效果是,反轉一個陣列,如:

let arr = [1,2,3,4,5]
console.log(arr); // [1,2,3,4,5]
console.log(arr.reverse()); // [5,4,3,2,1]

我們發現,反轉以後的陣列只不過是首尾對應位置顛倒了罷了,換句話說,就是位置交換,位置交換,位置交換

那麼,一說到陣列的位置交換,我們會想到哪幾種方式呢?

1.使用臨時變數交換, 如下:

let arr = ['甲', '乙']
let temp;
temp = arr[0]
arr[0] = arr[1]
arr[1] = temp
console.log(arr); // ['乙','甲']
氣泡排序的感覺...

2.使用ES6的解構賦值進行操作, 如下:

let arr = ['甲', '乙']
arr = ([arr[0], arr[1]] = [arr[1], arr[0]]); // 即:arr = [arr[0], arr[1]] = [arr[1], arr[0]]
console.log(arr); // ['乙', '甲']

透過上述方式,我們發現,既然是交換對應位置的兩個元素,那麼只要這兩個元素的索引我們們知曉即可。在心中默唸,兩個元素的索引、兩個元素的索引、兩個...

哎,有了,雙指標啊!

雙指標方式

  • 我們定義兩個變數,left和right,分別來表示對應的、需要交換位置的元素的、索引。
  • 然後一開始left值是0,right的值是arr.length-1。即為要交換第一個和最後一個位置的值。
  • 透過上文中交換陣列位置的方式交換完畢以後,說明第一個和最後一個已經交換完成了
  • 然後就交換第二個,和倒數第二個
  • 然後繼續交換第三個,和倒數第三個
  • 即為left遞增,right遞減
  • 但是交換總有停下來的時候(結束條件)
  • 當left等於right時(奇數項陣列)或者left大於right時(偶數項陣列)就不再繼續交換了
  • 也就是說,只要不滿足結束條件,我就繼續交換位置操作
  • 換句話說,只要處在條件內,我就持續執行操作
  • 只要符合xxx條件,就繼續執行操作(直到不符合條件時停下來不操作了),我們想到了while迴圈
奇數項陣列中間位的那一項不用動,偶數項是全部兩兩交換一下

程式碼(臨時變數交換)

var reverseString = function (s) {
        let left = 0 // 左側指標初始值為0,表示從第一項開始
        let right = s.length - 1 // 右側指標初始值為length-1,表示從最後一項開始
        while (left <= right) { // 當左側的指標小於右側指標時,說明還沒有交換完畢
                // 使用臨時變數交換位置
                let temp
                temp = s[left]
                s[left] = s[right]
                s[right] = temp
                // 交換完畢以後左側指標遞增,右側指標遞減
                left = left + 1
                right = right - 1
        }
        return s
};

程式碼(ES6解構交換)

var reverseString = function (s) {
    let left = 0 // 左側指標初始值為0,表示從第一項開始
    let right = s.length - 1 // 右側指標初始值為length-1,表示從最後一項開始
    while (left <= right) { // 當左側的指標小於右側指標時,說明還沒有交換完畢
        // 使用ES6解構交換位置
        [s[left], s[right]] = [s[right], s[left]]
        // 交換完畢以後左側指標遞增,右側指標遞減
        left = left + 1
        right = right - 1
    }
    return s
};

提交LeetCode結果圖

  • 使用temp臨時變數,速度更快,但是記憶體多耗費了一些
  • 使用ES6解構賦值交換位置,雖然速度慢了一點點,但是記憶體省下來一些了
  • 這不是重點,重點是雙指標的方式

相關文章