原位旋转指不借助额外数组、仅通过元素交换实现数组循环移位;不能单用std::reverse是因为它只能整体或局部翻转,而轮转需三段反转组合:先全反,再反前k,再反后n−k。

什么是原位旋转,为什么不能直接用 std::reverse 一次搞定
原位旋转数组(比如向右轮转 k 位)的核心约束是:不申请额外数组空间,只靠交换元素完成。很多人第一反应是 std::reverse,但单次调用只能翻转区间,无法直接实现循环移位——比如 [1,2,3,4,5] 向右轮转 2 位变成 [4,5,1,2,3],不是整体反转,而是三段反转的组合结果。
关键在于理解轮转本质:它等价于将数组拆成两块,交换它们的位置。而「交换两块」可通过三次反转高效模拟,且每步都原地进行、时间 O(n)、空间 O(1)。
三步反转法的具体操作和边界处理
以向右轮转 k 位为例(假设数组长度为 n):
- 先取模:
k = k % n,避免无效整圈旋转;若k == 0,直接返回 - 反转整个数组:
std::reverse(arr, arr + n) - 反转前
k个元素:std::reverse(arr, arr + k) - 反转后
n - k个元素:std::reverse(arr + k, arr + n)
示例:[1,2,3,4,5],n=5,k=2:
① 全反转 → [5,4,3,2,1]
② 前2反转 → [4,5,3,2,1]
③ 后3反转 → [4,5,1,2,3] ✅
注意:std::reverse 的迭代器范围是左闭右开,务必核对第二个参数是否为终点地址(不是长度)。
立即学习“C++免费学习笔记(深入)”;
手写交换循环时最容易错的索引越界和周期计算
如果不用 std::reverse,改用手写「环状替换」(cyclic replacement),就必须手动追踪置换环。这时常见错误包括:
- 没对
k和n求最大公约数:gcd(n, k)决定了环的数量,否则会重复覆盖或遗漏 - 内层循环终止条件写成
count 但未重置起始位置,导致只跑一个环就退出 - 计算下一个下标用
(curr + k) % n,但没把curr更新为该值,造成死循环 - 临时变量保存的是旧值,但赋值顺序错乱,例如先覆盖
arr[next]再读它
环状法虽节省函数调用开销,但代码更脆;除非有极致性能要求且 k 特别小,否则三步反转更稳妥。
vector 和 raw array 的处理差异与 const 正确性
用 std::vector 时,传入 vec.data() 给 std::reverse 是安全的(C++11 起保证连续存储);但若函数参数是 const std::vector<int>&</int>,你就无法修改——原位旋转本身意味着输入必须是非 const 的可写容器。
对于 raw array,注意类型匹配:int arr[5] 传给 std::reverse 需显式转成指针:std::reverse(arr, arr + 5);若误写 std::reverse(arr, arr + sizeof(arr)),就会越界。
另外,如果数组元素类型重载了移动构造/赋值(比如含动态内存的类),三步反转仍适用,但要确保移动操作是 noexcept 的,否则异常安全可能出问题。
真正容易被忽略的是:当 k 为负数(表示左旋)时,不要硬套公式,统一转成等效右旋:k = (k % n + n) % n,避免负模运算在不同编译器下的歧义。


















