題目描述
編寫一個函式,其作用是將輸入的字串反轉過來。輸入字串以字元陣列 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解構賦值交換位置,雖然速度慢了一點點,但是記憶體省下來一些了
- 這不是重點,重點是雙指標的方式