在 LeetCode 的字符串专题中,第 151 题《Reverse Words in a String》 是一道经典且实用的算法题。它不仅考察对字符串操作的理解,还涉及边界条件处理、空间效率优化等工程思维。本文将使用 Go 语言,从问题分析、多种解法到性能对比,带你彻底掌握这道题的核心思路。
一、题目描述
给你一个字符串
s,请你翻转字符串中单词的顺序。
- 单词是由非空格字符组成的字符串。
s中使用至少一个空格将字符串中的单词分隔开。- 返回单词顺序颠倒 且单词之间用单个空格连接的结果字符串。
- 注意 :输入字符串可能包含前导、尾随或多个连续空格 ,但输出中不能包含多余空格。
示例:
ini
输入: s = " hello world "
输出: "world hello"
输入: s = "a good example"
输出: "example good a"
二、解题思路分析
关键挑战在于:
- 去除多余空格(首尾 + 中间连续空格)
- 按单词分割
- 逆序拼接
我们可以从两个方向思考:
- 使用内置函数(快速实现,适合面试初期)
- 手动双指针处理(空间 O(1),展示底层能力)
三、方法一:使用 strings 包(简洁高效)
Go 的标准库 strings 提供了强大的文本处理能力。
go
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,展示对字符串遍历的掌控力。
go
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 开发中对文本处理的熟练度。