最省心的是std::rotate,右移k位需用v.end()-k作中轴点并先对k取模;三次翻转法为:整体翻转→前k个翻转→后n−k个翻转;insert+erase易致内存重分配且不安全。

用 std::rotate 最省心,但得注意方向和迭代器范围
直接调 std::rotate 是 C++ 标准库最稳妥的做法,它原地完成、时间复杂度 O(n)、稳定且无越界风险。关键点在于:右移 K 位等价于“以倒数第 K 个元素为新起点”进行左旋——即把 [begin, end-K) 和 [end-K, end) 两段交换位置。
常见错误是传错第三个参数:写成 std::rotate(v.begin(), v.begin() + k, v.end()) 就变成左移了;右移必须用 v.end() - k 作中轴点。
- 先对 K 取模:
k = k % n(n 为数组长度),避免无效旋转 - 若 k == 0,直接跳过,
std::rotate不检查这个,但空转没必要 - 对原始数组操作时,确保
v.end() - k是合法迭代器(k ≤ n)
手写三次翻转,理解原理且兼容 C 风格数组
三次翻转法本质是:右移 K 位 = 先整体翻转,再翻转前 K 个,再翻转后 n−K 个。它不依赖 STL,适合纯 C 数组或嵌入式环境,也便于调试边界。
容易漏掉的是中间两次翻转的起始/结束下标计算。比如数组 a[0..n-1],右移 K 位后,原末尾 K 个元素(下标 n-K 到 n-1)要移到前面,所以第二次翻转是 reverse(a, a + k),不是 a + n - k。
- 第一次:翻转整个区间
reverse(a, a + n) - 第二次:翻转前 K 个
reverse(a, a + k) - 第三次:翻转后 n−K 个
reverse(a + k, a + n)
示例:数组 [1,2,3,4,5] 右移 2 位 → 先变 [5,4,3,2,1] → 再翻前 2 个得 [4,5,3,2,1] → 再翻后 3 个得 [4,5,1,2,3] ✅
用 vector 的 insert + erase 容易内存重分配,慎用
有人会想:把后 K 个 insert 到开头,再 erase 掉末尾。语法上可行,但实际会触发多次内存拷贝甚至重新分配,性能差,且对非随机访问容器(如 list)也不通用。
更严重的问题是:如果 K > size(),erase 或 insert 可能抛 std::out_of_range 异常,而 std::rotate 和三次翻转都只依赖有效迭代器范围,天然安全。
- 仅当 K 已严格校验且 size 很小(
- 必须写
v.insert(v.begin(), v.end() - k, v.end()),不是v.insert(v.begin(), v.end() - k)(后者是单个元素) -
v.erase(v.end() - k, v.end())后,原末尾 K 个已不存在,别再访问
循环右移在 C 风格数组里必须手动算下标,别硬套 STL
面对 int arr[N] 这类栈数组,没有 begin()/end(),也不能直接传给 std::rotate(除非转成指针)。此时最直白的方式是开临时数组复制,或用三次翻转——但翻转函数得自己写,且注意指针运算边界。
典型坑:写 for (int i = 0; i 看似简洁,但 <code>(i - k + n) % n 中的 +n 是为了防负数取模,在某些编译器下若 i - k 是负的 long,% 行为可能不符合预期(尤其开启严格符号检查时)。
- 推荐用
((i - k) % n + n) % n或直接判断:int src = i - k - 如果空间敏感,坚持原地三次翻转,写一个通用
void reverse(int* a, int l, int r)(闭区间)更可控 - 别忘了 K 要先
k %= n,否则arr[n-k]可能越界
三次翻转的边界条件、std::rotate 的中轴点、以及 C 数组下标偏移的符号处理,这三个地方出错概率最高。动手前先拿长度为 3、K=4 的例子纸上演算一遍,比读十遍文档管用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











