三次反转法可实现O(1)空间、O(n)时间原地循环移位:先整体反转,再反转前k个,最后反转后n−k个;k需先对n取模,k==0时直接返回。

用三次反转法实现 O(1) 空间、O(n) 时间的原地循环移位
直接结论:C++ 中最实用、最易写错但性能最优的方案是「三次反转法」,它不依赖额外数组或 vector,连一个临时变量都只用常数个(比如 std::swap 内部用的),真正满足原地(in-place)要求。
核心思路是把循环移动拆解为三步反转:先整体反转,再反转前 k 个,最后反转后 n−k 个。数学上等价于旋转操作,且每一步都能用 std::reverse 或手写双指针完成。
- 假设数组
arr长度为n,要向右循环移动k位(k可能大于n,需先取模:k %= n) - 若
k == 0,直接返回,避免无谓操作 - 三次调用
std::reverse即可:std::reverse(arr, arr + n);<br>std::reverse(arr, arr + k);<br>std::reverse(arr + k, arr + n);
- 手写双指针版更可控(尤其在不能用 STL 的嵌入式场景):
auto reverse = [](int* l, int* r) {<br> while (l < r) std::swap(*l++, *--r);<br>};<br>reverse(arr, arr + n);<br>reverse(arr, arr + k);<br>reverse(arr + k, arr + n);
为什么不用逐个搬移(环状替换)?它容易出错
环状替换法理论上也是 O(1) 空间,但实际编码中极易陷入边界错误:比如未处理多环情况、计数漏掉、起始点选错导致部分元素没被覆盖。尤其当 gcd(n, k) != 1 时,数组会被分成多个独立置换环,必须显式跟踪已访问位置或用额外布尔数组——这就破了「不占额外内存」的前提。
- 典型错误现象:
arr = [1,2,3,4,5],右移 2 位,结果变成[4,5,1,2,3]是对的;但如果手写环算法时忘记重置起点,可能卡在第一个环里,漏掉第 3 个元素 -
std::rotate底层其实就用的环状替换(或优化后的混合策略),但它封装了所有细节;自己实现时除非有极致性能压榨需求(比如内核驱动),否则没必要重复造轮子 - 三次反转法逻辑线性、分支少、无状态变量,调试时一眼能看出哪步反了
使用 std::rotate 是最稳妥的生产选择
别自己造轮子。C++ 标准库的 std::rotate 就是专为这个场景设计的,接口清晰,行为明确,且现代 libstdc++/libc++ 都做了充分优化(小数组用 memmove,大数组用环状替换或分治反转)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 用法极简:
std::rotate(arr, arr + n - k, arr + n); // 向右移 k 位<br>// 注意:第三个参数是 end 迭代器,第二个是 new_first
- 参数顺序容易记混:它是「把
[first, middle)搬到后面,[middle, last)搬到前面」,所以右移 k 位对应middle = arr + n - k - 支持任意 RandomAccessIterator,不仅限于原生数组;对
std::vector、std::deque同样高效 - 时间复杂度稳定 O(n),空间 O(1),且异常安全(不抛异常)
注意 k 的符号和取模陷阱
左移和右移本质相同,但符号处理不统一就会出 bug。C++ 没有内置负数取模的「正余数」语义,-2 % 5 在多数编译器下是 -2 而非 3,直接用于索引会越界。
- 安全做法:统一转成非负等效偏移:
k = ((k % n) + n) % n;
,确保k ∈ [0, n) - 如果输入
k是编译期常量且确定为正,可跳过;但函数接口若接受运行时int k,这步不能省 - 对空数组(
n == 0)或单元素数组,所有方法都应短路返回,避免std::reverse或std::rotate处理空区间时潜在的未定义行为(虽然标准保证安全,但防御性编程更稳)
真正难的不是算法本身,而是把 k 归一化、把边界条件写全、以及信任标准库而不是反复手写反转逻辑。只要这三点做扎实,原地循环移动就没坑。

















