LeetCode 151. 反转字符串中的单词

leetcode.cn/problems/re...

programmercarl.com/0151.%E7%BF...

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意: 输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

ini 复制代码
输入: s = "the sky is blue"
输出: "blue is sky the"

示例 2:

ini 复制代码
输入: s = "  hello world  "
输出: "world hello"
解释: 反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

ini 复制代码
输入: s = "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示:

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ' '
  • s至少存在一个 单词

进阶: 如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

这道题目可以说是综合考察了字符串的多种操作。

一些同学会使用split库函数,分隔单词,然后定义一个新的string字符串,最后再把单词倒序相加,那么这道题题目就是一道水题了,失去了它的意义。

所以这里我还是提高一下本题的难度:不要使用辅助空间,空间复杂度要求为O(1)。

不能使用辅助空间之后,那么只能在原字符串上下功夫了。

想一下,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

举个例子,源字符串为:"the sky is blue "

  • 移除多余空格 : "the sky is blue"
  • 字符串反转:"eulb si yks eht"
  • 单词反转:"blue is sky the"

这样我们就完成了翻转字符串里的单词。

使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。

java 复制代码
void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
    int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.html
    for (int i = 0; i < s.size(); ++i) { //
        if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
            if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
            while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                s[slow++] = s[i++];
            }
        }
    }
    s.resize(slow); //slow的大小即为去除多余空格后的大小。
}
kotlin 复制代码
class Solution {
    /**
     * 1.去除首尾以及中间多余空格
     * 2.反转整个字符串
     * 3.反转各个单词
     */
    fun reverseWords(s: String): String {
        // 1.去除首尾以及中间多余空格
        val sb: StringBuilder = removeSpace(s)
        // 2.反转整个字符串
        reverseString(sb, 0, sb.length - 1)
        // 3.反转各个单词
        reverseEachWord(sb)
        return sb.toString()
    }

    private fun removeSpace(s: String): StringBuilder {
        var start = 0
        var end = s.length - 1
        while (s[start] == ' ') start++
        while (s[end] == ' ') end--
        val sb = StringBuilder()
        while (start <= end) {
            val c = s[start]
            if (c != ' ' || sb.get(sb.length - 1) != ' ') {
                sb.append(c)
            }
            start++
        }
        return sb
    }

    /**
     * 反转字符串指定区间[start, end]的字符
     */
    fun reverseString(sb: StringBuilder, start: Int, end: Int) {
        var start = start
        var end = end
        while (start < end) {
            val temp = sb.get(start)
            sb.setCharAt(start, sb[end])
            sb.setCharAt(end, temp)
            start++
            end--
        }
    }

    private fun reverseEachWord(sb: StringBuilder) {
        var start = 0
        var end = 1
        val n = sb.length
        while (start < n) {
            while (end < n && sb[end] != ' ') {
                end++
            }
            reverseString(sb, start, end - 1)
            start = end + 1
            end = start + 1
        }
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1) 或 O(n),取决于语言中字符串是否可变
相关推荐
mCell3 小时前
Lua 编程入门:从基础语法到元表
javascript·算法·lua
wuyk5555 小时前
98.C语言易混难点:字符数组与字符串指针的底层差异
c语言·开发语言·c++·stm32·嵌入式硬件·算法
峥无6 小时前
从0到1手撕红黑树:封装实现 my_map 与 my_set(SGI-STL 源码级深度解析)
开发语言·c++·笔记·算法·stl
不会代码的小猴6 小时前
3. 控件学习1
开发语言·c++·笔记·qt·算法
203号居民8 小时前
LeetCode hot 100 —41. 缺失的第一个正数
数据结构·算法·leetcode
专注仿真10 小时前
问答大模型技术方案算法实现-熵权法融合算法 + 交叉编码器重排算法
人工智能·python·算法·语言模型·问答大模型关键算法·熵权融合算法·交叉编码器重排算法
地平线开发者11 小时前
量化为什么会有损失:从量化误差到精度下降
算法
手写码匠12 小时前
华为云Flexus+DeepSeek征文|Dify 多 Agent 会话管理与上下文工程实战:让智能体“记住该记住的,忘掉该忘掉的“
人工智能·深度学习·算法·aigc
2401_8628808212 小时前
数据结构 --- 栈
c语言·数据结构·算法