[leetcode刷題筆記] 反轉字符串

  • 題目地址: https://leetcode-cn.com/problems/reverse-string/
  • 題目內容: 編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。 不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 你可以假設數組中的所有字符都是 ASCII 碼錶中的可打印字符。

示例:

  1. 輸入:[“h”,“e”,“l”,“l”,“o”]
    輸出:[“o”,“l”,“l”,“e”,“h”]
  2. 輸入:[“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]);
       	}
};

其優點有:

  1. 採用標準庫函數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);
    }
};

遞歸版本的時間複雜度和迭代版本的一樣,同爲O(n)\mathcal{O}(n),但是空間複雜度卻是O(n)\mathcal{O}(n),因爲遞歸需要利用程序棧儲存中間變量(比如參數傳遞時候的left+1right-1變量),導致了內存的浪費。


容易產生的問題

  1. 在進行指針聲明時,避免聲明成無符號的類型unsigned int,無符號類型在進行減法right-1時,如果是空輸入字符串,則s.size() = 0,減一就溢出了,導致bug。
  2. 在遞歸版本中,swap(s, left+1, right-1)可以寫成swap(s,++left, --right), 但是不能寫成swap(s, left++, right--),因爲後者是先把left, right的變量值給函數使用後,再進行增加減少,這個和我們的預期不一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章