关注
文末的名片达文汐
,回复关键词“力扣源码”,即可获取完整源码!!详见:源码和核心代码的区别
题目详情
给你一个字符串 s
,请你反转字符串中单词的顺序。
单词是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的单词分隔开。
返回单词顺序颠倒且单词之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = “the sky is blue”
输出:“blue is sky the”
示例 2:
输入:s = " hello world "
输出:“world hello”
解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
提示:
1 <= s.length <= 10^4
s
包含英文大小写字母、数字和空格' '
s
中至少存在一个单词
进阶:
如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1)
额外空间复杂度的原地解法。
解题思路
-
去除多余空格:
- 使用双指针法(
slow
和fast
)原地去除多余空格。 fast
指针遍历原字符数组,slow
指针指向当前有效位置。- 遇到非空格字符时,如果当前不是第一个单词(
slow != 0
),则在单词前添加一个空格。 - 复制整个单词到
slow
指针位置,并移动指针。 - 遇到空格时,直接跳过。
- 使用双指针法(
-
反转整个字符串:
- 对处理后的字符串(长度为
len
)进行整体反转,使单词顺序颠倒。
- 对处理后的字符串(长度为
-
反转每个单词:
- 遍历反转后的字符串,使用双指针标记单词的起始和结束位置。
- 遇到空格或字符串末尾时,反转当前单词,并更新下一个单词的起始位置。
-
返回结果:
- 根据字符数组前
len
个字符构建结果字符串。
- 根据字符数组前
代码实现(Java版)
class Solution {
public String reverseWords(String s) {
if (s == null) return null;
char[] charArray = s.toCharArray();
int n = charArray.length;
// 步骤1: 去除多余空格,返回新长度
int len = trimSpaces(charArray, n);
if (len == 0) return "";
// 步骤2: 反转整个字符串
reverse(charArray, 0, len - 1);
// 步骤3: 反转每个单词
reverseEachWord(charArray, len);
return new String(charArray, 0, len);
}
// 去除多余空格,返回新长度
private int trimSpaces(char[] chars, int n) {
int slow = 0;
int fast = 0;
while (fast < n) {
if (chars[fast] != ' ') {
// 非第一个单词时添加空格
if (slow != 0) {
chars[slow++] = ' ';
}
// 复制整个单词
while (fast < n && chars[fast] != ' ') {
chars[slow++] = chars[fast++];
}
} else {
fast++; // 跳过空格
}
}
return slow;
}
// 反转字符数组指定区间 [left, right]
private void reverse(char[] chars, int left, int right) {
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
// 反转每个单词
private void reverseEachWord(char[] chars, int len) {
int start = 0; // 单词起始位置
for (int end = 0; end <= len; end++) {
// 遇到空格或字符串末尾时反转单词
if (end == len || chars[end] == ' ') {
reverse(chars, start, end - 1);
start = end + 1; // 更新下一个单词起始位置
}
}
}
}
代码说明
-
trimSpaces
方法:- 使用双指针
slow
和fast
原地去除多余空格。 - 当
fast
遇到非空格字符时,若非第一个单词则在当前单词前添加空格,并复制整个单词。 - 时间复杂度
O(n)
,空间复杂度O(1)
(原地操作)。
- 使用双指针
-
reverse
方法:- 反转字符数组的指定区间
[left, right]
。 - 使用双指针从两端向中间交换字符。
- 反转字符数组的指定区间
-
reverseEachWord
方法:- 遍历处理后的字符串,标记单词起始位置
start
。 - 当遇到空格或字符串末尾时,反转
[start, end-1]
区间的单词。 - 更新
start
为下一个单词起始位置。
- 遍历处理后的字符串,标记单词起始位置
-
主方法
reverseWords
:- 将字符串转为字符数组。
- 依次执行:去空格 → 整体反转 → 单词反转。
- 返回新字符串(仅前
len
个有效字符)。