- 題目地址: https://leetcode-cn.com/problems/reverse-string/
- 題目內容: 編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。 不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 你可以假設數組中的所有字符都是 ASCII 碼錶中的可打印字符。
示例:
- 輸入:[“h”,“e”,“l”,“l”,“o”]
輸出:[“o”,“l”,“l”,“e”,“h”]- 輸入:[“H”,“a”,“n”,“n”,“a”,“h”]
輸出:[“h”,“a”,“n”,“n”,“a”,“H”]
該題要求要原地修改輸入數組,並且採用O(1)的額外空間,這就意味着不能用新的數組進行逆索引的方式解決,可以考慮的有雙向指針,一個指向數組的頭部,一個指向尾部,交換頭部和尾部的元素後,隨後更新這兩個指針即可。數組長度有兩種可能,奇數長度或者偶數長度,如果是偶數長度,則剛好能更新到left = right-1
這情況(該情況下還能正常的交換,下一個時刻是left-1 = right
,應該退出交換元素的過程了),如果是奇數,則會更新到left = right
的情況,無論是left-1 = right
還是left = right
,都符合在left >= right
的情況下退出元素交換,根據這個,我們可以寫出程序:
class Solution {
public:
void reverseString(vector<char>& s) {
int left = 0, right = s.size()-1;
while (left < right){
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++;
right--;
}
}
};
這個邏輯是對的,但是速度還不夠快,LeetCode上有個更快的例子爲:
class Solution {
public:
void reverseString(vector<char>& s) {
int left, right;
for (left = 0, right = s.size()-1;left < right;left++,right--){
swap(s[left], s[right]);
}
};
其優點有:
- 採用標準庫函數
std::swap()
進行元素交換,效率更高,避免了顯式地進行中間變量的聲明。
當然,我們也可以寫出遞歸版本的代碼,如:
class Solution {
public:
void helper(vector<char> &s, int left, int right){
if (left < right){
swap(s[left], s[right]);
helper(s, left+1, right-1);
}
}
void reverseString(vector<char>& s) {
helper(s, 0, s.size()-1);
}
};
遞歸版本的時間複雜度和迭代版本的一樣,同爲,但是空間複雜度卻是,因爲遞歸需要利用程序棧儲存中間變量(比如參數傳遞時候的left+1
和right-1
變量),導致了內存的浪費。
容易產生的問題:
- 在進行指針聲明時,避免聲明成無符號的類型
unsigned int
,無符號類型在進行減法right-1
時,如果是空輸入字符串,則s.size() = 0
,減一就溢出了,導致bug。 - 在遞歸版本中,
swap(s, left+1, right-1)
可以寫成swap(s,++left, --right)
, 但是不能寫成swap(s, left++, right--)
,因爲後者是先把left, right
的變量值給函數使用後,再進行增加減少,這個和我們的預期不一致。