Master array rotation completely — left and right rotation, three different algorithms (brute force, cyclic replacement, reversal method), their time and space complexities, circular array problems, and classic interview problems based on rotation.
Introduction
Array rotation is one of the most frequently tested array topics in coding interviews. At first glance, rotating an array seems trivial — shift everything by one. But doing it efficiently (O(n) time, O(1) space) requires insight that reveals deeper understanding of array manipulation.
This lesson covers: what rotation is, all three rotation algorithms with analysis, the classic reversal trick, circular arrays, and interview problems that build on rotation concepts.
Algorithm 1 — Brute Force (One Position at a Time)
Idea: Rotate by 1 position, k times.
def rotate_left_one(arr):
"""Rotate left by exactly one position."""
n = len(arr)
temp = arr[0] # Save first element
for i in range(n - 1):
arr[i] = arr[i + 1] # Shift everything left by 1
arr[n - 1] = temp # Place saved element at end
def rotate_left_brute(arr, k):
"""Rotate left by k positions using k single rotations."""
n = len(arr)
k = k % n # Handle k >= n
for _ in range(k):
rotate_left_one(arr)
arr = [1, 2, 3, 4, 5, 6, 7]
rotate_left_brute(arr, 3)
print(arr) # [4, 5, 6, 7, 1, 2, 3]
Analysis:
- rotate_left_one: O(n) — shifts n-1 elements
- Called k times → O(n × k)
- Worst case k = n/2 → O(n²)
Time: O(n × k), Space: O(1)
This is inefficient for large k. We can do better.
Algorithm 2 — Using Auxiliary Array
Idea: Copy rotated elements into a new array, then copy back.
def rotate_left_aux(arr, k):
"""
Rotate left by k using an auxiliary array.
Time: O(n), Space: O(n)
"""
n = len(arr)
k = k % n
temp = arr[k:] + arr[:k] # Build rotated version
for i in range(n):
arr[i] = temp[i] # Copy back
arr = [1, 2, 3, 4, 5, 6, 7]
rotate_left_aux(arr, 3)
print(arr) # [4, 5, 6, 7, 1, 2, 3]
# Cleaner Python one-liner (but creates a new array)
def rotate_left_python(arr, k):
n = len(arr)
k = k % n
return arr[k:] + arr[:k]
print(rotate_left_python([1, 2, 3, 4, 5, 6, 7], 3))
# [4, 5, 6, 7, 1, 2, 3]
Time: O(n), Space: O(n) — fast but uses extra memory.
Algorithm 3 — Reversal Algorithm (Optimal)
Idea: Three reversals achieve rotation in O(n) time and O(1) space.
For left rotation by k:
1. Reverse the first k elements
2. Reverse the remaining (n-k) elements
3. Reverse the entire array
def reverse_segment(arr, start, end):
"""Reverse arr[start..end] in-place."""
while start < end:
arr[start], arr[end] = arr[end], arr[start]
start += 1
end -= 1
def rotate_left_reversal(arr, k):
"""
Left rotate by k using the reversal algorithm.
Time: O(n), Space: O(1)
"""
n = len(arr)
k = k % n # Handle k >= n
if k == 0:
return
reverse_segment(arr, 0, k - 1) # Step 1: Reverse first k
reverse_segment(arr, k, n - 1) # Step 2: Reverse remaining
reverse_segment(arr, 0, n - 1) # Step 3: Reverse entire array
arr = [1, 2, 3, 4, 5, 6, 7]
rotate_left_reversal(arr, 3)
print(arr) # [4, 5, 6, 7, 1, 2, 3]
Step-by-step visualization:
| Original | [1, 2, 3, 4, 5, 6, 7] |
| Step 1 — reverse [0,2] | [3, 2, 1, 4, 5, 6, 7] |
| Step 2 — reverse [3,6] | [3, 2, 1, 7, 6, 5, 4] |
| Step 3 — reverse [0,6] | [4, 5, 6, 7, 1, 2, 3] ✓ |
For right rotation by k:
def rotate_right_reversal(arr, k):
"""
Right rotate by k using the reversal algorithm.
Right rotate k = Left rotate (n - k)
"""
n = len(arr)
k = k % n
if k == 0:
return
reverse_segment(arr, 0, n - k - 1) # Step 1: Reverse first (n-k)
reverse_segment(arr, n - k, n - 1) # Step 2: Reverse last k
reverse_segment(arr, 0, n - 1) # Step 3: Reverse entire array
arr = [1, 2, 3, 4, 5, 6, 7]
rotate_right_reversal(arr, 3)
print(arr) # [5, 6, 7, 1, 2, 3, 4]
Why does this work?
Left rotate by k means: elements at positions k, k+1, ..., n-1 come first, then 0, 1, ..., k-1.
The reversal algorithm achieves this through a clever mathematical property of reversals. If we define A = arr[0..k-1] and B = arr[k..n-1], we want B+A.
- After reversing A: get A'
- After reversing B: get B'
- After reversing A'B': get (B')(A')' = B+A ✓ (reverse of a concatenation reverses each part and swaps order)
Time: O(n) — three passes, each O(n). Space: O(1) — only swap variable.
Algorithm 4 — Cyclic Replacement
Idea: Place each element directly in its final rotated position.
def rotate_left_cyclic(arr, k):
"""
Rotate by placing each element in its correct final position.
Time: O(n), Space: O(1) — but complex to implement correctly.
"""
n = len(arr)
k = k % n
if k == 0:
return
count = 0 # Total elements placed
start = 0
while count < n:
current = start
prev_value = arr[start]
while True:
next_pos = (current - k + n) % n # Where this element goes after right-rotate
# (For left rotate by k, element at pos i goes to pos (i-k+n)%n)
temp = arr[next_pos]
arr[next_pos] = prev_value
prev_value = temp
current = next_pos
count += 1
if current == start: # Completed a cycle
break
start += 1
# Note: This is the algorithm LeetCode problem 189 ("Rotate Array") expects
arr = [1, 2, 3, 4, 5, 6, 7]
rotate_left_cyclic(arr, 3)
print(arr) # [4, 5, 6, 7, 1, 2, 3]
Time: O(n), Space: O(1) — same as reversal but more complex to reason about.
Comparison of All Algorithms
| Algorithm | Time | Space | Difficulty | Best For |
|---|
| Brute Force (k passes) | O(n×k) | O(1) | Easy | k = 1 or very small |
| Auxiliary Array | O(n) | O(n) | Easy | Readability, Python |
| Reversal Algorithm | O(n) | O(1) | Medium | Interviews (optimal) |
| Cyclic Replacement | O(n) | O(1) | Hard | Advanced interviews |
The reversal algorithm is the standard interview answer. It achieves optimal O(n) time and O(1) space with a clear, explainable pattern.
Edge Cases to Handle
def rotate_left_safe(arr, k):
"""Handles all edge cases."""
n = len(arr)
# Edge case 1: Empty array
if n == 0:
return arr
# Edge case 2: Single element — rotation has no effect
if n == 1:
return arr
# Edge case 3: k larger than n — normalize
k = k % n
if k == 0: # Edge case 4: k is 0 or multiple of n
return arr
# Proceed with rotation
reverse_segment(arr, 0, k - 1)
reverse_segment(arr, k, n - 1)
reverse_segment(arr, 0, n - 1)
return arr
Circular Arrays — Rotation Applied to Circular Structure
A circular array treats the array as if the end connects back to the beginning. Element at index n-1 is followed by element at index 0.
def get_circular(arr, index):
"""Access element in circular array."""
return arr[index % len(arr)]
arr = [1, 2, 3, 4, 5]
print(get_circular(arr, 0)) # 1
print(get_circular(arr, 4)) # 5
print(get_circular(arr, 5)) # 1 (wraps around)
print(get_circular(arr, 7)) # 3 (7 % 5 = 2)
print(get_circular(arr, -1)) # 5 (Python handles negative naturally)
Problem — Maximum Sum in Circular Array:
Given a circular array, find the maximum subarray sum (where the subarray can wrap around).
def max_circular_subarray_sum(arr):
"""
Two cases:
1. Maximum subarray does NOT wrap around → standard Kadane's
2. Maximum subarray WRAPS around → total_sum - min_subarray_sum
"""
def kadanes(arr):
max_ending_here = max_so_far = arr[0]
for x in arr[1:]:
max_ending_here = max(x, max_ending_here + x)
max_so_far = max(max_so_far, max_ending_here)
return max_so_far
def kadanes_min(arr):
min_ending_here = min_so_far = arr[0]
for x in arr[1:]:
min_ending_here = min(x, min_ending_here + x)
min_so_far = min(min_so_far, min_ending_here)
return min_so_far
max_no_wrap = kadanes(arr)
total = sum(arr)
min_subarray = kadanes_min(arr)
max_wrap = total - min_subarray
# If all elements are negative, max_wrap = 0 (wrapping gives empty subarray)
if max_wrap == 0:
return max_no_wrap
return max(max_no_wrap, max_wrap)
print(max_circular_subarray_sum([8, -8, 9, -9, 10, -11, 12])) # 22
print(max_circular_subarray_sum([10, -3, -4, 7, 6, 5, -4, -1])) # 23
Classic Interview Problems
Problem 1 — Check if Array is Rotation of Another
def is_rotation(arr1, arr2):
"""
Check if arr2 is a rotation of arr1.
Key insight: arr2 is a rotation of arr1 iff arr2 is a subarray of arr1+arr1.
"""
if len(arr1) != len(arr2):
return False
doubled = arr1 + arr1 # Concatenate arr1 with itself
# Check if arr2 appears as a subarray in doubled
n = len(arr1)
# Simple O(n²) check (KMP algorithm can do this in O(n))
for i in range(n):
if doubled[i:i+n] == arr2:
return True
return False
print(is_rotation([1,2,3,4,5], [3,4,5,1,2])) # True
print(is_rotation([1,2,3,4,5], [1,3,2,4,5])) # False
Problem 2 — Find Rotation Count in Sorted Rotated Array
def find_rotation_count(arr):
"""
A sorted array was rotated k times. Find k.
The rotation count equals the index of the minimum element.
Use binary search for O(log n).
"""
n = len(arr)
left, right = 0, n - 1
# If array is not rotated (already sorted)
if arr[left] <= arr[right]:
return 0
while left < right:
mid = (left + right) // 2
if arr[mid] > arr[right]:
left = mid + 1 # Minimum is in right half
else:
right = mid # Minimum is in left half or at mid
return left # Index of minimum = rotation count
print(find_rotation_count([4, 5, 6, 7, 1, 2, 3])) # 4 (rotated 4 times)
print(find_rotation_count([1, 2, 3, 4, 5])) # 0 (not rotated)
print(find_rotation_count([2, 1])) # 1
Problem 3 — Search in Sorted Rotated Array
def search_rotated(arr, target):
"""
Binary search in a sorted array that has been rotated.
O(log n) time.
"""
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
# Left half is sorted
if arr[left] <= arr[mid]:
if arr[left] <= target < arr[mid]:
right = mid - 1 # Target in sorted left half
else:
left = mid + 1 # Target in right half
# Right half is sorted
else:
if arr[mid] < target <= arr[right]:
left = mid + 1 # Target in sorted right half
else:
right = mid - 1 # Target in left half
return -1
print(search_rotated([4, 5, 6, 7, 0, 1, 2], 0)) # 4
print(search_rotated([4, 5, 6, 7, 0, 1, 2], 3)) # -1
Summary
- Left rotation by k: First k elements move to the end
- Right rotation by k: Last k elements move to the front
- Three algorithms: Brute force O(nk), Auxiliary O(n) space O(n), Reversal O(n) O(1)
- Reversal algorithm is the standard interview answer — optimal in both time and space
- Always normalize k: Use
k = k % n to handle k ≥ n - Circular arrays use modular indexing
arr[i % n] - Rotation-based problems appear frequently — searching in rotated sorted arrays, finding rotation count, checking rotation equality
*Next Lesson: Array Searching — Linear search, binary search, interpolation search, and search-based interview problems.*