在字符串处理类算法题中,LeetCode 151 题"反转字符串中的单词"是一道经典入门题。它不仅考察对字符串基本操作的掌握,还能延伸出"API 调用"与"手动模拟"两种核心解题思路,适合不同场景下的应用。本文将详细拆解题目要求,深入分析两种解法的原理、代码细节及优劣差异,帮助大家彻底掌握这道题。
一、题目核心要求
题目给出一个字符串 s,要求反转其中单词的顺序,同时需满足以下约束条件:
-
单词定义:由非空格字符组成的连续字符串,单词间用至少一个空格分隔;
-
输入特性:可能存在前导空格、尾随空格、单词间多个空格;
-
输出要求:单词顺序颠倒,单词间仅用单个空格分隔,无任何额外空格。
示例:输入 " hello world ",输出 "world hello";输入 "a good example",输出 "example good a"。
二、解法一:API 快速实现(简洁高效)
1. 解题思路
利用 JavaScript/TypeScript 内置字符串、数组 API,三步完成需求:
-
trim():去除字符串首尾的空格,解决前导、尾随空格问题; -
split(/\s+/):以一个或多个空格为分隔符拆分字符串,得到无空元素的单词数组(解决单词间多空格问题); -
reverse().join(' '):反转单词数组,再用单个空格连接,得到最终结果。
2. 代码实现(TypeScript)
typescript
function reverseWords_1(s: string): string {
const sArr = s.trim().split(/\s+/);
return sArr.reverse().join(' ');
};
3. 解法分析
优点 :代码极度简洁,开发效率高,内置 API 经过优化,在大多数场景下性能足够优秀。拆分时使用正则表达式 /\s+/ 精准匹配多空格,避免拆分后出现空数组元素,无需额外过滤。
缺点:过度依赖 API,若在面试中仅给出此解法,可能无法体现对字符串底层操作的理解;正则表达式的执行成本略高于纯循环(但在常规字符串长度下可忽略)。
时间复杂度 :O(n),其中 n 为字符串长度。trim()、split()、reverse()、join() 均为线性时间操作。
空间复杂度:O(n),拆分后得到的单词数组需占用 O(n) 额外空间。
二、解法二:手动模拟(底层逻辑实现)
1. 解题思路
不依赖复杂 API,通过反向遍历字符串手动拼接单词,全程仅维护两个变量(结果字符串、临时单词字符串),核心逻辑:
-
从字符串末尾开始反向遍历,逐个读取字符;
-
遇到非空格字符时,拼接到临时单词字符串的头部(因反向遍历,需保证单词内部顺序正确);
-
遇到空格且临时单词非空时,说明已完整读取一个单词,将其拼接到结果字符串后,重置临时单词;
-
遍历结束后,若临时单词非空(说明第一个单词未拼接),补充到结果字符串,最后去除首尾可能存在的空格(仅尾部可能有多余空格)。
2. 代码实现(TypeScript)
typescript
function reverseWords_2(s: string): string {
const sL = s.length;
let res = '';
let word = '';
for (let i = sL - 1; i >= 0; i--) {
const char = s[i];
if (char !== ' ') {
// 非空格字符,拼接到单词头部(维持单词原顺序)
word = char + word;
} else if (word) {
// 遇到空格且有已拼接单词,存入结果并重置单词
res += word + ' ';
word = '';
}
}
// 补充最后一个单词(遍历结束时word可能非空)
if (word) {
res += word;
}
return res.trim();
};
3. 关键细节说明
-
反向遍历的优势:无需先拆分所有单词再反转,可边遍历边构建结果,逻辑更直观;
-
单词拼接方式:
word = char + word而非word += char,因为反向遍历读取单词的顺序是"末尾→开头",拼接到头部才能还原单词原顺序; -
空格判断条件:
else if (word)避免了多个空格连续出现时重复拼接空内容,同时跳过字符串开头(反向遍历的末尾)的空格; -
遍历后补充单词:因字符串开头(反向遍历的末尾)无空格,最后一个单词无法通过"遇到空格"触发拼接,需单独处理。
4. 解法分析
优点:完全体现底层逻辑,适合面试场景,能展示对字符串操作的深刻理解;无正则表达式开销,空间利用率更优(可视为 O(1) 额外空间,仅用两个变量,忽略结果存储的空间)。
缺点:代码行数较多,需注意边界条件(如单个单词、全空格字符串),容易出现逻辑漏洞。
时间复杂度:O(n),仅遍历一次字符串,所有操作均为常数时间。
空间复杂度:O(n),结果字符串和临时单词字符串的总长度不超过原字符串长度。
三、两种解法对比与适用场景
| 维度 | API 解法 | 手动模拟解法 |
|---|---|---|
| 代码简洁度 | 极高,一行核心逻辑 | 中等,需处理边界条件 |
| 底层理解体现 | 弱,依赖内置 API | 强,手动实现核心逻辑 |
| 性能 | 优秀,API 优化充分 | 略优,无正则开销 |
| 适用场景 | 日常开发、快速迭代 | 面试、底层逻辑验证 |
四、边界案例测试
无论哪种解法,都需覆盖以下边界案例,确保正确性:
-
字符串仅含一个单词:输入
"hello",输出"hello"; -
字符串全为空格:输入
" ",输出""; -
单词间多个空格:输入
"a b c ",输出"c b a"; -
字符串长度为 1:输入
"a",输出"a",输入" ",输出""。
五、总结
LeetCode 151 题的两种解法各有侧重:API 解法胜在简洁高效,适合实际开发中的快速实现;手动模拟解法胜在底层逻辑清晰,是面试中的加分项。
核心考点在于"处理空格"与"反转单词顺序",无论哪种解法,都需明确:① 如何去除多余空格;② 如何保证单词内部顺序正确;③ 如何反转单词间的顺序。掌握这两个解法,既能应对日常开发需求,也能从容应对面试中的深度提问。