直接用 std::rotate 实现循环移位,其本质是通过三次反转利用“反转可逆且保序”特性完成位移;右移 k 位即反转[0,n−1]、[0,k−1]、[k,n−1]三段,左移则等价于右移 n−k 位。

直接用 std::rotate 就行,别手写三次翻转——除非你被面试官明确要求手撕算法,或者处理的是不可随机访问的缓冲区(比如 char* 且不能转 std::string)。
为什么三次翻转法能实现循环右移
核心不是“翻三次”,而是利用「反转操作可逆且保序」的特性,把一次整体位移拆解为三段局部顺序恢复。以 "abcdefg" 右移 3 位为例:
- 目标是把后 3 个字符
"efg"搬到前面,前 4 个"abcd"搬到后面 →"efgabcd" - 第一次反转整个字符串:
"gfedcba"→ 此时"efg"的逆序"gfe"已经在前段,"abcd"的逆序"dcba"在后段 - 第二次反转前 3 位:
"efgdcba"→"gfe"恢复为"efg" - 第三次反转后 4 位:
"efgabcd"→"dcba"恢复为"abcd"
本质是:右移 k 位 = 反转[0, n-1] + 反转[0, k-1] + 反转[k, n-1]。左移则把 k 换成 n-k 即可。
std::rotate 和手写三次翻转的性能差异在哪
std::rotate 在底层做了优化:对小范围用逐元素交换,对大块内存可能直接调用 memmove;而手写三次翻转固定执行三次遍历,常数因子略高。更重要的是:
-
std::rotate自动处理空串、k == 0、k >= size()等边界,无需额外取模或判空 - 手写翻转容易在索引计算出错,比如把
reverse(str, 0, k-1)写成reverse(str, 0, k),导致越界或少翻一位 - 对
std::string,std::rotate是原地操作,不触发内存重分配;而用substr+ 拼接会生成临时对象,O(n) 额外空间 + 两次拷贝
手写三次翻转时最容易漏掉的细节
如果你必须自己实现(比如嵌入式环境或面试题),这几个点一漏就挂:
-
k必须先做k %= n,否则当k > n时索引直接越界 —— 错误示例:reverse(arr, 0, k-1)中k=10、n=7会导致k-1 = 9超出数组范围 - 反转函数的区间是闭区间还是左闭右开?C++ 标准库的
reverse(begin, end)是左闭右开,但手写void reverse(char* s, int i, int j)通常按闭区间实现,调用时要统一,比如reverse(s, 0, k-1)对应前k个字符 - 对
char*,必须确保末尾有'\0',且反转后仍保持终止符在正确位置;若反转范围包含'\0',结果字符串会截断 - 左移和右移的参数映射别搞反:右移
k位等价于左移n - k位,但三次翻转的步骤顺序不能简单互换,必须按「整段→前段→后段」逻辑组织
真正难的不是写出三次翻转,而是想清楚哪一段该翻、翻几次、区间怎么切。一旦开始手动算下标,就离 std::rotate 的简洁差了一大截 —— 它已经把所有这些逻辑封进标准库了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











