刷题使我快乐,满脸开心.jpg
- 来源:力扣(LeetCode)
- 链接:https://leetcode.cn/problems/reverse-words-in-a-string/
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目
给你一个字符串 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 <= 104
s
包含英文大小写字母、数字和空格 ' 's
中 至少存在一个单词
进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1)
额外空间复杂度的 原地
解法。
思路
还没看到进阶时候,第一反应就是起一个新字符串,先处理掉多余的空格,然后整体反转后,再反转一个个单词即可。但是看到进阶之后,自然是要接受挑战了。
Go
的字符串不能直接用下标定位赋值,所以需要先转成byte
数组。其实Go
中这个强转过程会发生拷贝(可以打印地址验证下),也就是空间复杂度其实并不符合。不过想要实现无拷贝转换需要使用unsafe
包,感觉这里就不纠结这个问题了,主要是看下思路~
去除多余空格
开始我尝试在一次遍历中兼顾反转和过滤空格,不过发现心智负担太高,暂时搞不定,所以拆解成两个步骤,先去掉多余的空格。
多余就是指头尾空格,和单词之间多于一个的空格
头尾不用多说,主要问题就是在于中间空格的处理了。这里很常用的办法就是双指针法
了。
- 快指针先跨过头部空格
- 后续只往慢指针处赋予非空格和第一个空格,直到结束
- 直到结束遍历,尾部至多留有一个空格,再行判断去除即可
反转
反转就简单多了,直接首尾两个指针对向移动,交换元素直到相遇
思路差不多就这样了,细节在代码注释了,上代码~
代码
个人递归解法
func reverseWords(s string) string {
sBytes := []byte(s)
// 使用双指针去除空格
slow, fast := 0, 0
// 头部空格部分等待被实际内容替换即可,无需现在处理
for fast < len(sBytes) && sBytes[fast] == ' ' {
fast++
}
for fast < len(sBytes) {
// 非空格或者第一个空格,需要填充到头部
if sBytes[fast] != ' ' ||
(sBytes[fast] == ' ' && sBytes[fast-1] != ' ') {
sBytes[slow] = sBytes[fast]
fast++
slow++
} else if sBytes[fast] == ' ' && sBytes[fast-1] == ' ' {
// 中间多余的空格,需要跳过
fast++
}
}
// 看最后一个放进去的是不是空格,有则说明尾部有空格,截掉它
if slow > 0 && sBytes[slow-1] == ' ' {
sBytes = sBytes[:slow-1]
} else {
sBytes = sBytes[:slow]
}
// 整体反转字符串
reverse(&sBytes, 0, len(sBytes)-1)
// 逐步反转每个单词
start, end := -1, -1
// 把超出数组的下标放进来,兼容最后一个单词的情况
for i := 0; i <= len(sBytes); i++ {
// 最后一个单词的情况,外加遇到空格时,就是扫描到了一个单词,需要执行单词反转
if i == len(sBytes) || sBytes[i] == ' ' {
end = i - 1
reverse(&sBytes, start, end)
start = -1
end = 0
} else if sBytes[i] != ' ' && start == -1 {
// start未赋值时,第一次非空格就是新单词的起点
start = i
}
}
return string(sBytes)
}
func reverse(s *[]byte, start, end int) {
for start < end {
(*s)[start], (*s)[end] = (*s)[end], (*s)[start]
start++
end--
}
}