給定一個字串,逐個翻轉字串中的每個單詞。
範例 1:
輸入: "the sky is blue"
輸出: "blue is sky the"
範例 2:
輸入: " hello world! "
輸出: "world! hello"
解釋: 輸入字串可以在前面或者後面包含多餘的空格,但是反轉後的字元不能包括。
範例 3:
輸入: "a good example"
輸出: "example good a"
解釋: 如果兩個單詞間有多餘的空格,將反轉後單詞間的空格減少到只含一個。
以範例1為例,一種或很樸素的想法是:將字串整個反轉
"the sky is blue" ---> "eulb si yks eht"
然後再逐個將單詞轉回來
那麼就有問題了,對於輸入格式規範的字串我們可以想上面那樣處理
但是題目所給的輸入有可能是不規範的
也就是說,字串的前面、後間都有可能插入多個空格,反轉之後還要將這些空格去除(也就是把格式統一為標準形式)
這就有點難搞
現在大致可以把解題思路分為三部分:
1、將字串轉換為標準形式(去除多餘的空格,僅保留單詞間的必要空格)
2、將標準字串整體反轉
3、將反轉字串中的單詞反轉回正常順序
按照思路來看,都寫在主函數裡不太現實,可以先把「去除空格」函數和「反轉字串」函數分別寫出來
就根據思路來寫
和以前一樣,仍需要明確快慢指標的用途
void deleteExtraSpaces(string& s){
//定義慢指標
int slow = 0;
//遍歷字串
for(int fast = 0; fast < s.size(); ++fast){//++i和i++僅在效率上不同
if(s[fast] != ' '){
//如果當前字元(單詞)不是第一個字元(單詞),那麼要在單詞前面加空格
if(slow != 0){
s[slow] = ' ';
slow++;
}
//此時找到了單詞的開頭,需要將其按字元逐個移動到slow所指的位置
//迴圈條件中需要再判斷當前fast是否指向空格,指到就停止移動
//進入下一個for迴圈,fast指標從道歉位置後移一位
while(fast < s.size() && s[fast] != ' '){
s[slow] = s[fast];
slow++;
fast++;
}
}
}
//因為去除了多餘的空格,所以需要重新計算一下陣列的大小
s.resize(slow);//此時slow即為標準化後字串陣列的長度
}
上述程式碼的過程圖示如下:
這裡 for 有個坑,後面注意事項的時候細說
和 反轉字串 裡面用的一樣,當然也可以用c++提供的
注意:該函數可以反轉一條字串中指定位置的字元
void reverse(string& s, int start, int end){//注意要輸入字串的參照!!!
//在for中同時維護兩個變數
for(int i = start, j = end; i < j; i++, j--){
swap(s[i], s[j]);
}
}
在主函數裡面使用這兩個函數完成解題邏輯
class Solution {
public:
void deleteExtraSpaces(string& s){
//定義慢指標
int slow = 0;
//遍歷字串
for(int fast = 0; fast < s.size(); ++fast){//++i和i++僅在效率上不同
if(s[fast] != ' '){
//如果當前字元(單詞)不是第一個字元(單詞),那麼要在單詞前面加空格
if(slow != 0){
s[slow] = ' ';
slow++;
}
//此時找到了單詞的開頭,需要將其按字元逐個移動到slow所指的位置
//迴圈條件中需要再判斷當前fast是否指向空格,指到就停止移動
//進入下一個for迴圈,fast指標從道歉位置後移一位
while(fast < s.size() && s[fast] != ' '){
s[slow] = s[fast];
slow++;
fast++;
}
}
}
//因為去除了多餘的空格,所以需要重新計算一下陣列的大小
s.resize(slow);//此時slow即為標準化後字串陣列的長度
}
void reverse(string& s, int start, int end){
//在for中同時維護兩個變數
for(int i = start, j = end; i < j; i++, j--){
swap(s[i], s[j]);
}
}
string reverseWords(string s) {
//1、將字串轉換為標準形式,去除空格
deleteExtraSpaces(s);
//2、字串整體反轉
reverse(s, 0, s.size() - 1);//記得減1
//3、恢復每個單詞的正常順序
//依舊使用快慢指標
int slow = 0;//start,fast即為end
for(int fast = 0; fast <= s.size(); ++fast){
if(fast == s.size() || s[fast] == ' '){//到達空格或者字串結尾倒要反轉,不然會漏翻
reverse(s, slow, fast - 1);
slow = fast + 1;//從下一個單詞的頭開始
}
}
return s;
}
};
正確的思考方式應該是想清楚思路之後,實現每個部分需要的函數,然後再一塊搭起來
本題中大量且靈活的體現了for迴圈的一些平時容易忽略的特性
for(int i = 0; i < xxx; i++){
}
上述是一個典型的for迴圈結構
要注意的是,開頭的這句for(int i = 0; i < xxx; i++)
它只對迴圈變數 i 進行初始化(賦初值),以及在每次{ }
內程式碼執行完畢後對迴圈變數執行迴圈條件(即i++
)
至於{ }
內發生了什麼,for迴圈本身是不管的。
也就是說,如果我在{ }
內對迴圈變數 i進行了操作,也是允許的
例如:
for(int i = 0; i < xxx; i++){
i = 5;
}//那麼下輪迴圈,i 就從6開始,而不是1
在本題中,大量運用了該特性,例如:擷取遍歷過程中的單詞
++i
和i++
如果你瞭解前++和後++的區別的話,那你應該知道後++的實現需要藉助一個臨時變數temp
實際上,在for的日常使用中,使用++i
還是i++
在結果上是沒有區別的
但是如果是刷題的話,++i
的執行效率會好一些,僅此而已