std::rotate执行循环左移,参数为rotate(first, middle, last),将[first, middle)移到[middle, last)之后;对vector/deque用三步翻转o(n),list可能指针调整更高效,forward_list不支持。

rotate 函数到底在转什么位置
std::rotate 不是按角度或方向旋转,而是把一个区间「循环左移」:以某个中间点为界,把前半段搬到后半段后面。比如 rotate(v.begin(), v.begin() + 2, v.end()) 表示“以第 2 个元素为 pivot,把 [begin, begin+2) 搬到 [begin+2, end) 后面”。结果等价于先取走前两元素,再追加到末尾。
关键要记住三个迭代器参数顺序:rotate(first, middle, last),其中 [first, middle) 是被挪走的前段,[middle, last) 是后段,最终变成 [middle, last) + [first, middle)。
vector 和 list 上调用 rotate 的性能差异
std::rotate 对随机访问容器(如 std::vector、std::deque)通常用三步翻转法(reverse 三次),时间复杂度 O(n),空间 O(1);对双向链表 std::list,标准库可能直接调整指针,同样 O(n) 时间但常数更小;而对单向链表(如 std::forward_list),rotate 无法高效实现,C++17 起已明确不支持——调用会编译失败。
- 用
vector时放心调用,无额外分配 - 用
list时注意:虽然接口存在,但某些老编译器(如 GCC 4.8)可能退化成拷贝,建议实测size()大时的耗时 - 别对
forward_list写rotate,编译报错信息通常是no matching function for call to 'rotate'
常见误用:middle 越界或迭代器失效
最常踩的坑是传错 middle —— 它必须落在 [first, last] 范围内(允许等于 last,此时等效于没动)。例如:
立即学习“C++免费学习笔记(深入)”;
vector<int> v = {1,2,3,4,5};
rotate(v.begin(), v.begin() + 10, v.end()); // UB!middle 超出 end
另一个陷阱是,在 rotate 过程中修改容器大小(如 push_back)会导致所有迭代器失效,而 rotate 的三个参数都是迭代器,一旦失效,行为未定义。
- 调用前用
assert(middle >= first && middle 防御性检查(调试模式下) - 避免在
rotate前后紧邻地调用erase或insert,尤其当用的是同一容器的迭代器时 - 如果 middle 来自 find 等算法结果,务必检查是否等于
last(即未找到),否则解引用会崩溃
替代方案:什么时候不该用 rotate
如果只是想把最后一个元素移到开头,或者把第一个移到末尾,用 rotate 是杀鸡用牛刀。更清晰的做法是:
- 末尾→开头:
v.insert(v.begin(), v.back()); v.pop_back(); - 开头→末尾:
v.push_back(v.front()); v.erase(v.begin());
这些操作语义直白,且对小容器(如 size rotate 内部至少涉及 2n 次赋值(三步翻转),在 tiny range 下反而有额外开销。只有当你需要任意切分点的循环移位,且容器较大时,rotate 的 O(n) 原地优势才真正体现出来。
真正容易被忽略的是:rotate 不改变容器 size,也不做任何类型转换或构造,它只重排已有对象的内存位置——所以如果元素类型有非平凡移动构造,性能取决于该类型的移动效率,而非 rotate 本身。










