C++代码随想录刷题知识分享-----反转字符串 —— 从基础题练出指针思维与空间优化技巧
一、题目介绍:Reverse String
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组
s
的形式给出,你必须 原地修改 输入数组,使用 O(1) 的额外空间。
✍️ 题目链接(LeetCode 344):
344. 反转字符串 - 力扣(LeetCode)
✅ 示例
输入:
s = ['h','e','l','l','o']
输出:
['o','l','l','e','h']
二、问题解析:原地反转的本质含义
这道题的核心并不在于“反转字符串”本身,而在于如何原地操作、如何使用最少空间做高效的字符交换。
📌 “原地” 是什么?
原地(in-place)算法是指:不依赖额外的数据结构,仅使用常数级别的额外空间对输入本身进行修改。这是许多底层算法设计中的重要约束,常见于:
- 数组操作(反转、旋转)
- 链表处理
- 排序算法(如堆排序、原地快排)
三、算法设计与思路:双指针法(Two Pointers)
我们可以设置两个指针:
left
从字符串头部开始;right
从字符串尾部开始。
不断交换 s[left]
和 s[right]
,然后双指针逐步向中间靠拢,直到两者相遇或交叉。
🧠 思维要点:
- 双指针在数组中是非常高效的遍历方式,常用于反转、滑动窗口、查找等问题。
- 每次交换两个字符,保证操作是原地进行。
- 时间复杂度 O(n),空间复杂度 O(1),满足题目约束。
四、C++ 实现代码
class Solution {
public:void reverseString(vector<char>& s) {// 使用两个指针分别指向字符串首尾for (int i = 0, j = s.size() - 1; i < j; ++i, --j) {// 交换字符 s[i] 与 s[j]char temp = s[i];s[i] = s[j];s[j] = temp;}}
};
✅ 关键注释说明:
代码片段 | 注释说明 |
---|---|
for (int i = 0, j = s.size() - 1; i < j; ++i, --j) | 双指针从两端向中间靠拢 |
char temp = s[i]; s[i] = s[j]; s[j] = temp; | 标准的交换操作 |
五、拓展思考:反转的几种变形题
📌 1. 反转字符串 II(LeetCode 541)
每隔 k
个字符反转前 k
个字符,考察字符串分块与边界处理。
📌 2. 反转字符串中的单词(LeetCode 151)
不仅要反转字符,还要处理空格、词序等,难度更高,需多步处理。
📌 3. 翻转链表(LeetCode 206)
思想一致,但应用在链表数据结构上,需小心指针操作。
六、底层原理拓展:交换的本质与优化技巧
💡 C++ 中字符交换的底层机制:
使用中间变量交换两个值是最常见的写法,但还有其他方式:
1. 使用 std::swap
(推荐)
#include <algorithm>
std::swap(s[i], s[j]);
优点:可读性强,由标准库提供,内部可能使用编译器优化。
2. 异或交换法(不推荐)
s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
虽不使用额外变量,但可读性差,且对某些类型(如浮点数)无效。
💡 空间优化讨论
当前算法已经达到空间复杂度 $O(1)$ 的最优解,没有使用任何辅助容器。这是“原地反转”的真正含义。
这在性能要求高的系统编程中非常重要,如:
- 内存受限场景(嵌入式)
- 对性能敏感的后端服务(网络字节序转换)
- 操作系统字符缓冲区管理等
七、总结与感悟:用简单问题训练扎实功底
虽然这是道简单题,但背后涉及的知识点非常丰富:
- 双指针的使用方式与场景;
- 如何写出“原地”操作的代码;
- 如何让代码高效且清晰(避免暴力复制或开辟新空间);
- 可扩展性强,可以作为许多复杂字符串题的基础模块。
📌 建议:初学者可从此题开始深入理解 C++ 中数组和字符串的底层操作,并尝试封装通用的反转函数模板,提高代码复用性。
附:代码性能对比(LeetCode 数据)
方法 | 运行时间 | 内存使用 |
---|---|---|
双指针交换法 | ✅ 99.7% | ✅ 89.1% |
STL reverse | ✅ 98.5% | ✅ 86.3% |
手动复制反转 | ❌ 较慢 | ❌ 空间大 |