原位交换旋转指不占额外空间、仅通过元素交换实现数组循环移位;std::rotate虽底层采用三次反转法实现原位旋转,但其接口语义是循环左移而非直接“交换”,且要求middle在[first,last)内,否则未定义行为。

什么是原位交换旋转,为什么不能直接用 std::rotate
原位交换旋转指对数组进行循环移位(如向右移动 k 位)时,不申请额外的 O(n) 空间,仅靠元素间交换完成。虽然 std::rotate 在大多数标准库实现中已是原位的(基于三次反转),但如果你在面试、嵌入式环境或自定义容器中需要明确控制逻辑,或想理解底层原理,就得自己写——而且得避开常见误区:比如只用单层循环导致错位、忽略 k 超出数组长度的情况。
三次反转法:最可靠且易验证的原位方案
核心思想是利用「反转」操作的可组合性:对整个数组反转,再分别反转前 k 和后 n−k 段,等价于向右旋转 k 位。它时间复杂度 O(n),交换次数严格 ≤ n,无分支预测失败风险,且对任意 k 都健壮。
实操注意点:
-
k必须先取模:k = k % n,否则反转逻辑会越界或行为异常 - 反转函数必须是左闭右开区间习惯(如
reverse(arr, 0, n)表示索引[0, n)),避免 off-by-one - 若用
std::vector,确保传入的是引用或指针,别意外拷贝
简短示意:
void reverse(int* arr, int left, int right) {
while (left <h3>环状替换法:空间更省但边界条件多</h3><p>当内存极度受限(比如每个字节都要精打细算),且你确定 <code>k</code> 与 <code>n</code> 的最大公约数 <code>gcd(n,k)</code> 较小,可用环状替换:从下标 <code>0</code> 开始,把元素依次跳 <code>k</code> 步搬运,直到回到起点,构成一个环;重复 <code>gcd(n,k)</code> 轮。它实际交换次数恰好是 <code>n</code>,但代码逻辑更绕。</p><p>容易踩的坑:</p>
- 没正确计算轮数:必须是
std::gcd(n, k),不是k或n/k - 内层循环终止条件写成
next != start却忘了更新next,导致死循环 - 没保存起始值的备份,搬运中途覆盖未读取元素
- 使用
int计算(i + k) % n时,若k很大可能溢出(应转为size_t或加防溢处理)
什么时候该选哪种方法?
日常开发优先用三次反转——它逻辑直白、调试友好、CPU cache 友好(顺序访问),std::rotate 底层就是它。环状替换只在两类场景值得手写:一是题目明确要求“最小交换次数”或“只能用常数额外变量”,二是你在写某个硬件寄存器映射数组,连栈上临时变量都受严格限制。
真正容易被忽略的是:无论哪种方法,k 为负数或 n == 0 时的行为。C++ 标准里 % 对负数结果依赖实现,务必显式归一化:k = ((k % n) + n) % n,否则在不同编译器上可能旋转方向相反。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











