std::rotate是高效原地循环移位的最优解:在o(n)时间、o(1)空间内完成,通过三步翻转实现,支持vector/array/原始指针;右移k步调用std::rotate(first, last-k, last),左移k步调用std::rotate(first, first+k, last),且k必须先对长度取模防越界。

用 std::rotate 实现高效原地移位
直接调用 std::rotate 是最稳妥的选择,它在 O(n) 时间、O(1) 空间内完成任意步数的循环移位,且不依赖数组是否连续(支持 vector、array、原始指针)。关键不是“左移右移”,而是“以某位置为界旋转”:std::rotate(first, new_first, last) 把 [first, new_first) 挪到后面,[new_first, last) 挪到前面。
比如右移 k 步:等价于以 end - k 为分割点旋转;左移 k 步:等价于以 begin + k 为分割点旋转。务必先对 k 取模,避免越界或无效操作:
std::vector<int> arr = {1,2,3,4,5};
int k = 2;
k %= arr.size(); // 必须!否则 rotate 行为未定义
// 右移 2 → {4,5,1,2,3}
std::rotate(arr.begin(), arr.end() - k, arr.end());
// 左移 2 → {3,4,5,1,2}
std::rotate(arr.begin(), arr.begin() + k, arr.end());</int>
手写三步翻转法——不依赖 STL 时的通用解法
当不能用 std::rotate(如嵌入式环境、裸机编程),或想理解底层原理,三步翻转是标准做法:左移 k 等价于「整体翻转 → 前 n−k 翻转 → 后 k 翻转」;右移则交换后两步顺序。核心是复用一个 reverse 辅助函数,避免额外空间。
常见错误是边界计算出错,尤其 k 超出数组长度或为 0 时未处理:
-
k %= n必须做,否则k > n会导致reverse参数非法 - 翻转区间用左闭右开约定(
[l, r)),和 STL 保持一致,减少混淆 - 注意
reverse(arr, 0, k)这类调用中,k 是长度而非下标,需转换
示例(右移 k):
void reverse(int* arr, int l, int r) {
while (l <h3>原始数组指针移位时的常见陷阱</h3><p>用 <code>int arr[5]</code> 这类栈数组时,别试图通过指针算术“假装移动”:比如 <code>int* shifted = arr + 2</code> 看似右移了,但访问 <code>shifted[0]</code> 到 <code>shifted[4]</code> 会越界——它只是起始地址变了,不代表逻辑上完成了循环移位。</p><p>真正需要移位时,必须复制数据或就地重排。另一个坑是误把移位当成截断:右移常被误解为“丢弃末尾、补 0”,但题干中的“右移”默认指循环右移(即末尾元素绕回开头),除非明确要求补零填充(此时应改用 <code>std::copy_backward</code> + 填充)。</p>
- 循环移位 ≠ 截断移位:前者长度不变,后者长度可能变
- 原始数组传参时,
sizeof(arr)在函数内失效,必须显式传长度 - 用
std::array可保留长度信息,避免手动传参出错
性能与适用场景对比:什么时候该选哪种方法
std::rotate 经过高度优化,对小数组用内置循环,对大数组可能用内存块搬运(如 memmove),实际性能通常优于手写。但若目标平台无 STL 支持(如某些 RTOS),或编译器禁用异常/RTTI 导致 std::rotate 链接失败,则三步翻转更可控。
如果只是临时取“移位后的视图”而无需修改原数组,可封装一个代理类或用 std::span(C++20)配合自定义迭代器,避免拷贝——但这已超出“实现移位”的范畴,属于接口设计权衡。
真正容易被忽略的是:移位步数 k 的符号处理。C++ 中负的 k 没有默认含义,必须自己约定(比如 k k = ((k % n) + n) % n,否则 -1 % 5 在 C++ 中结果是 -1,直接传给 rotate 会崩溃。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











