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),取决于语言中字符串是否可变
相关推荐
a1117763 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜4 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry4 小时前
LeetCode 541. 反转字符串 II
算法
Jerry4 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手4 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议
Java小白笔记4 小时前
Codex config.toml配置实战指南
人工智能·算法·chatgpt·ai编程·集成学习
王老师青少年编程5 小时前
2026年6月GESP真题及题解(C++七级):消消乐
数据结构·c++·算法·真题·gesp·2026年6月
z小猫不吃鱼5 小时前
模型剪枝经典论文精读:Pruning Filters for Efficient ConvNets
算法·机器学习·剪枝
Fox爱分享6 小时前
字节二面:1000瓶酒,有一瓶是毒药,多少只老鼠可以查出来?
算法·面试·程序员