Go 实战 LeetCode 151:高效翻转字符串中的单词(含空格处理技巧)

Go 实战 LeetCode 151:高效翻转字符串中的单词(含空格处理技巧)

在 LeetCode 的字符串专题中,第 151 题《Reverse Words in a String》 是一道经典且实用的算法题。它不仅考察对字符串操作的理解,还涉及边界条件处理、空间效率优化等工程思维。本文将使用 Go 语言,从问题分析、多种解法到性能对比,带你彻底掌握这道题的核心思路。


一、题目描述

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

  • 单词是由非空格字符组成的字符串。
  • s 中使用至少一个空格将字符串中的单词分隔开。
  • 返回单词顺序颠倒单词之间用单个空格连接的结果字符串。
  • 注意 :输入字符串可能包含前导、尾随或多个连续空格 ,但输出中不能包含多余空格

示例:

复制代码
输入: s = "  hello world  "
输出: "world hello"

输入: s = "a good   example"
输出: "example good a"

二、解题思路分析

关键挑战在于:

  1. 去除多余空格(首尾 + 中间连续空格)
  2. 按单词分割
  3. 逆序拼接

我们可以从两个方向思考:

  • 使用内置函数(快速实现,适合面试初期)
  • 手动双指针处理(空间 O(1),展示底层能力)

三、方法一:使用 strings 包(简洁高效)

Go 的标准库 strings 提供了强大的文本处理能力。

复制代码
import (
    "strings"
)

func reverseWords(s string) string {
    // 1. 按空白字符分割(自动处理多个空格)
    words := strings.Fields(s)
    
    // 2. 反转切片
    for i, j := 0, len(words)-1; i < j; i, j = i+1, j-1 {
        words[i], words[j] = words[j], words[i]
    }
    
    // 3. 用单个空格连接
    return strings.Join(words, " ")
}

优点

  • 代码简洁,可读性强
  • strings.Fields 自动忽略所有空白(包括 \t, \n 等)
  • 时间复杂度:O(n),空间复杂度:O(n)

💡 面试建议:先写出此解法,再讨论是否允许使用内置函数。


四、方法二:原地处理(模拟 O(1) 空间,实际仍需新字符串)

虽然 Go 字符串不可变,无法真正"原地"修改,但我们可以通过双指针手动解析单词 ,避免使用 Fields,展示对字符串遍历的掌控力。

复制代码
func reverseWords(s string) string {
    var words []string
    n := len(s)
    i := 0

    for i < n {
        // 跳过空格
        for i < n && s[i] == ' ' {
            i++
        }
        if i >= n {
            break
        }

        // 收集单词
        j := i
        for j < n && s[j] != ' ' {
            j++
        }
        words = append(words, s[i:j])
        i = j
    }

    // 反转并拼接
    var res strings.Builder
    for i := len(words) - 1; i >= 0; i-- {
        if i < len(words)-1 {
            res.WriteByte(' ')
        }
        res.WriteString(words[i])
    }

    return res.String()
}

优点

  • 明确控制空格处理逻辑
  • 使用 strings.Builder 高效拼接(避免多次字符串拷贝)
  • 展示底层字符串处理能力

五、关键知识点总结

技术点 说明
strings.Fields(s) 按任意空白字符分割,自动过滤空字符串
strings.Join 高效拼接字符串切片
strings.Builder 推荐用于多次字符串拼接,避免内存浪费
双指针遍历 手动跳过空格、提取单词的经典技巧

六、常见误区提醒

  • ❌ 直接 strings.Split(s, " "):会保留空字符串(如 "a b"["a", "", "b"]
  • ❌ 忘记去除首尾空格:即使中间处理正确,首尾空格会导致结果错误
  • ❌ 多次 += 拼接字符串:在 Go 中会导致大量内存分配,应使用 Builder

七、总结

LeetCode 151 题看似简单,实则考察了字符串预处理、边界控制和工程实践的综合能力。在 Go 中:

  • 优先使用 strings.Fields + Join 实现清晰逻辑;
  • 若需展示底层能力,可用双指针 + Builder 手动实现;
  • 始终注意时间与空间效率,避免低效拼接。

掌握这类问题,不仅能通过面试,更能提升日常 Go 开发中对文本处理的熟练度。

📌 延伸思考:如果要求"原地翻转"(如 C++ 中修改 char 数组),该如何设计?(提示:先整体翻转,再逐个单词翻转)

相关推荐
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC2 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
To_OC3 天前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
To_OC3 天前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC4 天前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
To_OC6 天前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
To_OC7 天前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript
码云数智-园园13 天前
C++20 Modules 模块详解
java·开发语言·spring