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

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

这个问题要求将给定字符串中的单词顺序反转,同时移除多余空格,使得每个单词之间只有一个空格,且字符串首尾无空格。

方法思路

最优解法是原地反转法:通过三次反转操作实现单词顺序的反转,同时处理多余空格。具体步骤如下:

  1. 移除多余空格 :

    将字符串中的多余空格去除,只保留单词间的单个空格,并调整字符串长度。

  2. 整体反转 :

    将处理后的字符串整体反转。

  3. 单词反转 :

    遍历字符串,对每个单词进行局部反转,恢复单词的原始顺序。

C++ 代码实现

cpp 复制代码
#include <string>
#include <algorithm>

class Solution {
public:
    string reverseWords(string s) {
        // 1. 移除多余空格
        int i = 0, j = 0;  // i 为写入指针,j 为读取指针
        while (j < s.length()) {
            // 跳过前导空格
            while (j < s.length() && s[j] == ' ') j++;
            if (j >= s.length()) break;  // 处理全空格的情况
            
            // 添加单词和中间空格
            if (i != 0) s[i++] = ' ';  // 单词间添加一个空格
            while (j < s.length() && s[j] != ' ') {
                s[i++] = s[j++];
            }
        }
        s.resize(i);  // 调整字符串长度
        
        // 2. 整体反转
        reverse(s.begin(), s.end());
        
        // 3. 逐个单词反转
        i = 0;
        for (int j = 0; j <= s.length(); j++) {
            if (j == s.length() || s[j] == ' ') {  // 遇到空格或字符串末尾
                reverse(s.begin() + i, s.begin() + j);
                i = j + 1;  // 更新下一个单词的起始位置
            }
        }
        
        return s;
    }
};

代码解释

  1. 移除多余空格:

    • 使用双指针 i 和 j,其中 j 用于遍历原字符串,i 用于写入处理后的字符。
    • 跳过前导空格,遇到单词时将其复制到 i 位置,并在单词间添加单个空格。
    • 最终使用 resize(i) 调整字符串长度,去除尾部冗余字符。
  2. 整体反转 :

    使用 reverse(s.begin(), s.end()) 将整个字符串反转,此时单词顺序已反转,但每个单词内部字符顺序也被反转。

  3. 单词反转:

    • 遍历字符串,每当遇到空格或字符串末尾时,将当前单词(从 i 到 j-1)进行局部反转。
    • 更新 i 为下一个单词的起始位置(j+1)。

复杂度分析

  • 时间复杂度:O(n),其中 n 是字符串的长度。每个字符最多被处理三次(移除空格、整体反转、单词反转)。
  • 空间复杂度:O(1),仅需常数级的额外空间。

示例

输入:s = " hello world! "

输出:"world! hello"
解释:

  1. 移除多余空格 :"hello world!"
  2. 整体反转 :"!dlrow olleh"
  3. 单词反转 :"world! hello"

关键点

  1. 原地修改 :

    通过双指针法在原字符串上直接操作,避免额外空间开销。

  2. 三次反转策略 :

    整体反转+单词反转的组合巧妙地实现了单词顺序的反转,同时保持单词内容正确。

  3. 边界处理:

    • 处理字符串首尾的多余空格。
    • 处理连续多个空格的情况。
    • 处理空字符串或全空格字符串的情况。

这种方法高效且符合题目要求,是解决此类问题的经典思路。

方法二:

cpp 复制代码
class Solution {
public:
    string reverseWords(string s) {
        stringstream ss(s);

        vector<string> words;
        string word;

        while (ss >> word)
        {
            words.emplace_back(word);
        }
        
        string res = words[words.size() - 1];
        for (int i = words.size() - 2; i >= 0; --i)
        {
            res += " ";
            res += words[i];
        }

        return res;

    }
};
相关推荐
程序猿乐锅10 分钟前
万字详解Single-flight(单飞模式)
java·开发语言·分布式·后端·学习·single-flight
扶尔魔ocy11 分钟前
【QT入门到晋级】用开源的自定义控件开发
c++·qt·自定义控件
AiNightVision13 分钟前
无人机夜视模块如何选择?——从参数指标到系统集成的完整选型指南
人工智能·算法·计算机视觉·车载系统·无人机·视频·智能硬件
牧羊人.3331 小时前
卷积神经网络之VGG
人工智能·深度学习·神经网络·算法·cnn
akinggw1 小时前
libsherpacore - 基于 sherpa-onnx 的 C++ 语音交互静态库
c++·交互·语音识别
xiaoye-duck2 小时前
《C++ 项目:手写大模型接入 SDK》ChatGPT接口设计(中):全量响应实现和测试
网络·c++·语言模型·chatgpt
揽星河..2 小时前
【C语言】指针
c语言·开发语言
小郎君6582 小时前
Vue 多标签页的加载态设计:为什么我没有用 Suspense 实现 PageLoading
vue.js·算法
小小数媒成员2 小时前
UGUI原理(一)
开发语言·游戏·unity·游戏引擎
JJJennie7772 小时前
MAI Gateway私有化部署全攻略:大模型网关如何接入?附架构图
算法·gateway·ai网关·魔芋ai