Article / 文章
LeetCode 第186题:反转字符串中的单词 II
给你一个字符数组 s,反转该数组中单词的顺序。 单词的定义是:连续的非空格字符,单词的前后用空格字符隔开。单词中可能包含前导或尾随空格。 请你反转单词的顺序,但不要反转单词内字符的顺序。返回反转后的数组。
题目描述
给你一个字符数组 s,反转该数组中单词的顺序。
单词的定义是:连续的非空格字符,单词的前后用空格字符隔开。单词中可能包含前导或尾随空格。
请你反转单词的顺序,但不要反转单词内字符的顺序。返回反转后的数组。
难度
中等
题目链接
示例
示例 1:
输入:s = ["t","h","e"," ","s","k","y"," ","i","s"," ","b","l","u","e"]
输出:["b","l","u","e"," ","i","s"," ","s","k","y"," ","t","h","e"]
示例 2:
输入:s = ["a"]
输出:["a"]
提示
1 <= s.length <= 10^5s[i]可以是英文大小写字母、数字、空格' '、或者其他可见字符s中至少存在一个单词s不含前导或尾随空格- 所有单词都被空格分隔
解题思路
方法:先整体反转,再逐个单词反转
这道题与 LeetCode 151 “反转字符串中的单词” 类似,但要求直接在原数组上操作。解决方法是:先将整个字符数组反转,然后再逐个反转每个单词。
关键点:
- 将整个字符数组
s反转 - 遍历反转后的数组,对每个单词(连续的非空格字符)单独进行反转
- 由于要求原地修改数组,我们需要使用双指针技巧来实现反转操作
时间复杂度:O(n),其中 n 是字符数组的长度,我们只需要遍历数组常数次 空间复杂度:O(1),只需要常数额外空间
代码实现
C# 实现
public class Solution {
public void ReverseWords(char[] s) {
// 1. 反转整个字符数组
Reverse(s, 0, s.Length - 1);
// 2. 反转每个单词
int start = 0;
for (int end = 0; end < s.Length; end++) {
if (s[end] == ' ') {
Reverse(s, start, end - 1);
start = end + 1;
}
}
// 反转最后一个单词(最后没有空格)
Reverse(s, start, s.Length - 1);
}
private void Reverse(char[] s, int start, int end) {
while (start < end) {
char temp = s[start];
s[start] = s[end];
s[end] = temp;
start++;
end--;
}
}
}
Python 实现
class Solution:
def reverseWords(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
# 1. 反转整个字符数组
self.reverse(s, 0, len(s) - 1)
# 2. 反转每个单词
start = 0
for end in range(len(s)):
if s[end] == ' ':
self.reverse(s, start, end - 1)
start = end + 1
# 反转最后一个单词(最后没有空格)
self.reverse(s, start, len(s) - 1)
def reverse(self, s: List[str], start: int, end: int) -> None:
while start < end:
s[start], s[end] = s[end], s[start]
start += 1
end -= 1
C++ 实现
class Solution {
public:
void reverseWords(vector<char>& s) {
// 1. 反转整个字符数组
reverse(s.begin(), s.end());
// 2. 反转每个单词
int n = s.size();
int start = 0;
for (int end = 0; end < n; end++) {
if (s[end] == ' ') {
reverse(s.begin() + start, s.begin() + end);
start = end + 1;
}
}
// 反转最后一个单词(最后没有空格)
reverse(s.begin() + start, s.end());
}
};
性能分析
各语言实现的性能对比:
| 实现语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 296 ms | 44.2 MB | 使用自定义的反转函数 |
| Python | 180 ms | 18.9 MB | 利用Python的交换语法,简洁高效 |
| C++ | 16 ms | 15.6 MB | 使用标准库reverse函数,性能最优 |
补充说明
代码亮点
- 使用“先整体反转,再逐个单词反转”的两步法,算法简洁高效
- 利用双指针技巧实现原地反转,不需要额外空间
- C++实现中利用了标准库的reverse函数,简化了代码
为什么这种方法有效?
这种方法的巧妙之处在于利用了反转操作的性质:
- 如果我们将整个字符串反转,单词的顺序会反转,但每个单词内部的字符也会反转
- 然后,我们再单独反转每个单词,这样单词内部的字符顺序又恢复正常了
- 最终结果是:单词的顺序反转了,但单词内部的字符顺序保持不变
常见错误
- 忘记处理最后一个单词(最后一个单词后面没有空格)
- 反转单词的边界处理不当,导致单词反转不正确
- 对于单个字符或空数组的边界情况处理不当