原位循环移动的高效方法是三次反转法:先反转整个数组,再反转前k个元素,最后反转后n−k个元素,空间复杂度o(1)、时间复杂度o(n);需先对k取模并处理边界情况。

原位循环移动的核心思路:三次反转法
直接用额外数组或临时变量逐个搬移,空间复杂度是 O(n),不符合“不占辅助内存”要求。真正高效的做法是利用“反转”操作的数学性质:对数组 a[0..n-1] 向右循环移动 k 位,等价于先反转整个数组,再反转前 k 个元素,最后反转后 n-k 个元素。这个方法只用 O(1) 额外空间,且时间复杂度严格 O(n) —— 每个元素最多被访问两次。
关键点在于:k 要先对数组长度取模(k %= n),否则当 k > n 时逻辑错乱;若 n == 0 或 k == 0,直接返回,避免无意义操作。
手写反转函数要注意边界和索引闭合性
标准库有 std::reverse,但自己实现更利于理解细节和控制行为。常见错误是搞错区间端点:反转 [l, r] 时,应让 l 从左往右、r 从右往左,直到 l 不再成立;若写成 <code>l ,偶数长度时中间两个元素会被交换两次,结果不变,但奇数长度时中间元素会和自己交换一次——虽无害,但冗余。更稳妥的是统一用 <code>l 。
- 使用
std::swap是安全的,支持自定义类型(只要满足可交换) - 不要用算术加减做指针偏移(如
arr + i)来传参,除非你明确在处理裸指针;对std::vector应传迭代器或引用 - 如果移动距离
k很大(比如接近n),建议先转为左移等价形式:k = n - k,减少最后一次反转的长度,但不影响正确性
std::rotate 是最简实践方案,但要注意迭代器失效风险
如果你用的是 std::vector 或其他支持随机访问的容器,std::rotate 就是标准答案。它内部正是用三次反转实现的,接口清晰:std::rotate(v.begin(), v.begin() + k, v.end()) 表示以第 k 个元素为新起点旋转。但要注意:
-
k必须是合法偏移量(0 ),越界会导致未定义行为 - 对
std::list,std::rotate是线性时间但非原地“常数空间”——它会修改节点指针,不分配新节点,仍算原位,但算法不同(链表用拼接而非反转) - 若容器是
std::array或 C 风格数组,需传入原生指针:std::rotate(arr, arr + k, arr + n)
当元素移动涉及非平凡析构/构造时,必须用 std::move 或 placement new
如果数组里存的是带有资源管理逻辑的对象(比如含 std::string 成员的类),三次反转中频繁调用 std::swap 会触发多次拷贝构造和析构。此时应确保该类型支持移动语义,或显式用 std::move 配合 std::swap。更底层的情况(如手动管理内存的结构体),可能需要避开 std::swap,改用 memcpy + placement new + 显式析构,但这仅限 trivially copyable 类型,且极易出错。
一句话提醒:原位移动的“高效”前提是类型开销可控。别为了省几个字节内存,把移动成本从 O(n) 拉高到 O(n × 构造成本)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











