LeetCode 151. 反转字符串中的单词
摘要
LeetCode 151 反转字符串中的单词题解,采用整体反转 + 局部反转的方式,先反转整个字符串,再逐个反转每个单词,同时去除多余空格,时间复杂度 O(n),空间复杂度 O(1)。
标签
#算法 #LeetCode #字符串 #双指针 #题解
目录
- 题目描述
- 易错点
- 思路
- 编写代码
1. 题目描述
给你一个字符串 s,请你反转字符串中单词的顺序。
单词是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的单词分隔开。
返回单词顺序颠倒且单词之间用单个空格连接的结果字符串。
注意: 输入字符串 s 中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
text
输入:s = "the sky is blue"
输出:"blue is sky the"
示例 2:
text
输入:s = " hello world "
输出:"world hello"
解释:反转后的字符串不能包含前导和尾随空格。
示例 3:
text
输入:s = "a good example"
输出:"example good a"
解释:单词间的多个空格要缩减为单个空格。
提示:
1 <= s.length <= 10^4s包含英文大小写字母、数字和空格' 's中至少存在一个单词
2. 易错点
- 需要处理多余空格:前导空格、尾随空格、单词间的多个空格
- 反转整个字符串后,单词顺序颠倒,但每个单词内部的字符也是颠倒的,需要再次反转每个单词
- 在去除空格并填充新字符串时,需注意最后不要多添加一个空格
- 需要原地修改字符串并截断(在末尾添加
'\0') - 双指针移动时要防止越界,并正确处理指针位置
- 原字符串可能长度较大,需要保证算法时间复杂度 O(n)
3. 思路
采用整体反转 + 局部反转的方法,分为三步:
-
反转整个字符串:将字符串整体反转,此时单词顺序变为正确顺序,但每个单词内部的字符也是反的
-
去除多余空格并移动单词:
- 使用快慢指针
i和j遍历原字符串 i用于扫描原字符串,跳过所有空格找到单词开头j用于在新位置上重建字符串- 将单词字符复制到
j位置,然后在单词后添加一个空格(最后一个单词除外)
- 使用快慢指针
-
反转每个单词:
- 在刚刚构建的新字符串中,对每个单词(由空格分隔)进行反转,使其恢复正常顺序
-
截断字符串:最后在
j位置添加'\0',完成原地修改
该方法时间复杂度 O(n),空间复杂度 O(1),符合题目进阶要求。
4. 编写代码
c 运行
c
char* reverseWords(char* s) {
int len = strlen(s);
// 1. 反转整个字符串
for (int i = 0, j = len - 1; i < j; i++, j--) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
// 2. 去除多余空格并移动单词,同时反转每个单词
int i = 0, j = 0;
while (i < len) {
// 跳过空格,找到单词起始
while (i < len && s[i] == ' ') i++;
if (i >= len) break;
int start = j;
// 复制单词字符
while (i < len && s[i] != ' ') {
s[j++] = s[i++];
}
int end = j - 1;
// 反转当前单词
while (start < end) {
char tmp = s[start];
s[start] = s[end];
s[end] = tmp;
start++;
end--;
}
// 在单词后添加一个空格(最后一个单词后不加,稍后处理)
if (j < len) s[j++] = ' ';
}
// 去除末尾多余的空格
if (j > 0 && s[j-1] == ' ') j--;
s[j] = '\0';
return s;
}