Reverse Vowels of a String
題目描述
給定字串 s,只反轉其中的母音字母(a, e, i, o, u,含大小寫),其餘字元位置不變。
輸入:s = "hello"
輸出:"holle"
輸入:s = "leetcode"
輸出:"leotcede"
解題思路
1
轉為陣列
字串不可變,呼叫 toCharArray/split 轉為可修改的字元陣列
2
雙指針初始化
left = 0 指向最左端,right = len-1 指向最右端
3
向內移動
left 跳過非母音字元往右走,right 跳過非母音字元往左走,直到各自停在母音上
4
交換
left 和 right 都指向母音時,swap(arr[left], arr[right]),然後 left++ right--
5
回傳
字元陣列用 join 或 new string 轉回字串
程式碼
C#
csharp
public class Solution {
public string ReverseVowels(string s) {
var arr = s.ToCharArray();
var vowels = new HashSet<char>("aeiouAEIOU");
int left = 0, right = arr.Length - 1;
while (left < right) {
while (left < right && !vowels.Contains(arr[left])) left++;
while (left < right && !vowels.Contains(arr[right])) right--;
if (left < right) {
(arr[left], arr[right]) = (arr[right], arr[left]);
left++;
right--;
}
}
return new string(arr);
}
}TypeScript
typescript
function reverseVowels(s: string): string {
const arr = s.split("");
const vowels = new Set("aeiouAEIOU");
let left = 0, right = arr.length - 1;
while (left < right) {
while (left < right && !vowels.has(arr[left])) left++;
while (left < right && !vowels.has(arr[right])) right--;
if (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
}
return arr.join("");
}Python
python
def reverseVowels(s: str) -> str:
arr = list(s)
vowels = set("aeiouAEIOU")
left, right = 0, len(arr) - 1
while left < right:
while left < right and arr[left] not in vowels:
left += 1
while left < right and arr[right] not in vowels:
right -= 1
if left < right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
return "".join(arr)複雜度分析
| | Time | Space | |--|------|-------| | 本解法 | O(n) | O(n) |
left 和 right 合計最多走 n 步(每步至少移動一個指針),所以時間 O(n)。字元陣列複製整個字串,空間 O(n)。
邊界情況
- 沒有母音:例如
"bcdfg",雙指針找不到母音,直接交錯退出,字串不變 - 全部是母音:例如
"aeiou",等同於完整反轉字串,結果"uoiea" - 單一字元:
"a"或"b",left 和 right 相等或交錯,直接回傳原字串
圖解
h e l l o
雙指針搜尋母音→
left→e, right→o
找到配對→
交換 e ↔ o
完成→
h o l l e
圖表展示 "hello" 的處理過程:left 跳過 h 停在 e(index 1),right 從 o 開始就是母音(index 4)。交換後得到 "holle"。此例只有一對母音需要交換,一輪就完成。
進階觀點
💡面試追問
- 為什麼用 HashSet 而不是 if 判斷? 兩者查找都是 O(1)。HashSet 寫法更簡潔(
vowels.Contains(c)vs 一串|| c == 'a' || c == 'e'...),也更容易擴展(加新字元只需修改集合內容)。 - 如果要反轉子音呢? 完全一樣的雙指針邏輯,只要把
vowels.Contains改成!vowels.Contains,跳過母音、停在子音即可。 - 跟「反轉字串」的關係? 反轉字串是無條件交換所有字元,本題是「條件交換」的變體。雙指針框架相同,差別只在移動指針時是否跳過某些字元。
理解測驗
🤔 雙指針在這題中為什麼有效?
🤔 s = "aA" 的輸出是什麼?