Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Elementary Algorithms / Array Transformations

Rotates the half-open iterator range $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}})$ left around the split point mid, where ${\htmlClass{math-inline-code}{\texttt{lo}}} \leq {\htmlClass{math-inline-code}{\texttt{mid}}} \leq {\htmlClass{math-inline-code}{\texttt{hi}}}$. Writing the adjacent subranges $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{mid}}})$ and $[{\htmlClass{math-inline-code}{\texttt{mid}}}, {\htmlClass{math-inline-code}{\texttt{hi}}})$ as A and B, respectively, the operation transforms A B into B A while preserving the relative order within each subrange. The standard library function std::rotate(lo, mid, hi) performs the same rearrangement.

All three versions below operate in place, using different algorithms and iterator capabilities.

  • rotate1(lo, mid, hi) requires ForwardIterators and repeatedly swaps the next elements of the two unfinished subranges. When the right iterator reaches hi, it wraps to the current mid; when the left iterator reaches mid, that boundary advances to the right iterator.
  • rotate2(lo, mid, hi) requires BidirectionalIterators, applying a trick with three reversals. Writing the input subranges as A B, two initial reversals yield reverse(A) reverse(B); then the whole range is reversed to yield B A, restoring the order within each subrange.
  • rotate3(lo, mid, hi) requires random-access iterators, applying a juggling algorithm which first divides the range into gcd(hi - lo, mid - lo) sets and then rotates the corresponding elements in each set. The method follows the permutation that sends each position to its rotated destination. These form that many disjoint cycles, so walking each cycle once moves every element to its final position.

Implementation

#include <algorithm>
#include <numeric>

template<typename It>
void rotate1(It lo, It mid, It hi) {
  if (lo == mid || mid == hi) {
    return;
  }
  It next = mid;
  while (lo != next) {
    std::iter_swap(lo++, next++);
    if (next == hi) {
      next = mid;
    } else if (lo == mid) {
      mid = next;
    }
  }
}

template<typename It>
void rotate2(It lo, It mid, It hi) {
  if (lo == mid || mid == hi) {
    return;
  }
  std::reverse(lo, mid);
  std::reverse(mid, hi);
  std::reverse(lo, hi);
}

template<typename It>
void rotate3(It lo, It mid, It hi) {
  if (lo == mid || mid == hi) {
    return;
  }
  int n = static_cast<int>(hi - lo);
  int jump = static_cast<int>(mid - lo);
  int g = std::gcd(jump, n), cycle = n / g;
  for (int i = 0; i < g; i++) {
    int curr = i, next;
    for (int j = 0; j < cycle - 1; j++) {
      next = curr + jump;
      if (next >= n) {
        next -= n;
      }
      std::iter_swap(lo + curr, lo + next);
      curr = next;
    }
  }
}

Example Usage

#include <algorithm>
#include <cassert>
#include <vector>
using namespace std;

int main() {
  vector<int> a0, a1, a2, a3;
  for (int i = 0; i < 10000; i++) {
    a0.push_back(i);
  }
  a1 = a2 = a3 = a0;
  int mid = 5678;
  std::rotate(a0.begin(), a0.begin() + mid, a0.end());
  rotate1(a1.begin(), a1.begin() + mid, a1.end());
  rotate2(a2.begin(), a2.begin() + mid, a2.end());
  rotate3(a3.begin(), a3.begin() + mid, a3.end());
  assert(a0 == a1 && a0 == a2 && a0 == a3);

  vector<int> no_op{1, 2, 3};
  rotate1(no_op.begin(), no_op.begin(), no_op.end());
  assert((no_op == vector<int>{1, 2, 3}));
  rotate2(no_op.begin(), no_op.end(), no_op.end());
  assert((no_op == vector<int>{1, 2, 3}));
  rotate3(no_op.begin(), no_op.begin(), no_op.end());
  assert((no_op == vector<int>{1, 2, 3}));

  vector<int> a{2, 4, 2, 0, 5, 10, 7, 3, 7, 1};

  // Insertion sort.
  for (auto i = a.begin(); i != a.end(); ++i) {
    rotate1(upper_bound(a.begin(), i, *i), i, i + 1);
  }
  assert((a == vector<int>{0, 1, 2, 2, 3, 4, 5, 7, 7, 10}));

  // Simple rotation to the left.
  rotate2(a.begin(), a.begin() + 1, a.end());
  assert((a == vector<int>{1, 2, 2, 3, 4, 5, 7, 7, 10, 0}));

  // Simple rotation to the right.
  rotate3(a.rbegin(), a.rbegin() + 1, a.rend());
  assert((a == vector<int>{0, 1, 2, 2, 3, 4, 5, 7, 7, 10}));
  return 0;
}