LeetCode 151 題要求:給定一個輸入字串,按單詞順序反轉。例如 "the sky is blue" → "blue is sky the"。本文從暴力解法進階到 O(1) 額外空間的原地演算法,詳細分析字串處理的關鍵技巧與陷阱。
// 思路:分割單詞 → 反轉順序 → 拼接
// 缺點:每次拼接創建新 String 物件,O(n²) 時間,記憶體碎片嚴重
private String ReWords(String s) {
String result = " ";
for (int i = 0; i < s.length(); i++) {
int j = i;
while (j < s.length() && Character.isLetter(s.charAt(j))) j++;
String word = s.substring(i, j);
result = word + result; // 每次創建新字串!
i = j;
}
return result.trim();
}
public String reverseWords(String s) {
StringBuilder reversed = new StringBuilder();
int j = s.length(); // 單詞結束邊界(不包含)
// 從右向左掃描
for (int i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) == ' ') {
j = i; // 更新單詞結束位置
}
// 發現單詞開頭:i==0 或前一字符是空格
else if (i == 0 || s.charAt(i - 1) == ' ') {
if (reversed.length() != 0) {
reversed.append(' '); // 單詞間加空格
}
// substring(i, j) 取得 [i, j) 區間單詞
reversed.append(s.substring(i, j));
}
}
return reversed.toString();
}
j 記錄單詞結束位置,i 為掃描指標,遇到空格更新 j,遇到單詞開頭提取 s.substring(i, j)reversed 已有內容時追加空格,避免首尾空格Java String 不可變,需轉 char[] 操作,最後構建新 String(輸出不計入額外空間)。
public String reverseWords(String s) {
char[] arr = s.toCharArray();
int n = arr.length;
// 步驟 1:整體反轉字元陣列
reverse(arr, 0, n - 1);
// 步驟 2:反轉每個單詞內部字元順序
int start = 0, end = 0;
while (start < n) {
// 跳過空格
while (start < n && arr[start] == ' ') start++;
if (start >= n) break;
end = start;
while (end < n && arr[end] != ' ') end++;
// 反轉 [start, end-1]
reverse(arr, start, end - 1);
start = end;
}
// 步驟 3:清理多餘空格(雙指針原地壓縮)
int write = 0, read = 0;
while (read < n) {
// 跳過前導/連續空格
while (read < n && arr[read] == ' ') read++;
if (read >= n) break;
// 複製單詞
if (write > 0) arr[write++] = ' '; // 單詞間加單空格
while (read < n && arr[read] != ' ') {
arr[write++] = arr[read++];
}
}
return new String(arr, 0, write); // 截取有效部分
}
private void reverse(char[] arr, int l, int r) {
while (l < r) {
char tmp = arr[l];
arr[l++] = arr[r];
arr[r--] = tmp;
}
}
| 解法 | 時間複雜度 | 空間複雜度 | 備註 |
|---|---|---|---|
| 字串拼接 | O(n²) | O(n²) | 大量臨時物件,GC 壓力大 |
| StringBuilder | O(n) | O(n) | 標準解,生產環境推薦 |
| 原地 char[] | O(n) | O(1) 額外 | 面試進階要求,展示演算法功力 |
// 測試用例
reverseWords("the sky is blue") // "blue is sky the"
reverseWords(" hello world ") // "world hello"
reverseWords("a good example") // "example good a"
reverseWords("") // ""
reverseWords(" ") // ""
reverseWords("a") // "a"
reverseWords(" a b ") // "b a"
String.substring(int begin, int end):返回 [begin, end) 子串,不包含 end 索引StringBuilder.append():支援鏈式調用,內部擴容策略為 1.5 倍Character.isLetter(char):判斷是否字母,注意中文標點非字母String.toCharArray():複製字串內容,O(n) 空間String 不可變導致拼接效率低,推薦 StringBuilderchar 無法完整表示補充平面字符,生產環境建議用 codePoint 相關 APILeetCode 151 是字串處理經典題目,考察:
掌握從暴力解到最優解的演進思路,不僅能通過面試,更能養成寫出高效、健壯字串處理代碼的習慣。