Array Rotation
Array Rotation
Introduction
Array rotation is a fundamental operation that cyclically shifts the elements of an array by a specified number of positions, either to the left or right. This manipulation is crucial in various algorithmic problems, data processing, and cryptographic contexts. Understanding efficient array rotation techniques is key to optimizing solutions involving circular data structures or rearrangement tasks.
Quick Overview
Array rotation involves repositioning elements such that the original elements effectively 'wrap around' the array boundaries, maintaining their relative order but shifting their starting point.
Motivation
Array rotation appears frequently in competitive programming and real-world applications. From implementing circular buffers and simulating game board movements to processing data streams and image manipulations, the ability to efficiently rotate arrays is a valuable skill. It often serves as a foundational step or a critical insight for more complex problems, particularly those involving circular arrays or patterns that repeat across boundaries. Mastery of array rotation fosters a deeper understanding of in-place algorithms and modular arithmetic.
Real Life Analogy
Imagine a carousel with numbered horses, where each horse represents an element in an array. If the carousel rotates a certain number of steps to the right, the horse that was originally at position 1 might move to position 4, and the horse at the last position might wrap around to position 3. The horses themselves don't change, but their positions relative to a fixed observer do, much like elements in an array during rotation.
Core Idea
The core idea of array rotation is to reorder the elements of an array by shifting them cyclically. A 'right rotation' moves elements towards the end of the array, with elements 'falling off' the end wrapping around to the beginning. Conversely, a 'left rotation' moves elements towards the beginning, with elements 'falling off' the beginning wrapping around to the end.
Key Insight
One of the most elegant and efficient ways to perform array rotation is the 'Reversal Algorithm'. Instead of iteratively moving elements, which can be inefficient, we can achieve rotation by applying three strategic reversals: first, reverse the entire array; then, reverse the first 'k' elements; finally, reverse the remaining 'n-k' elements. This 'three-reversal' pattern dramatically reduces operations.
Prerequisites
- Basic understanding of arrays and array indexing.
- Concept of in-place algorithms.
- Modular arithmetic (for handling k > n).
- Understanding of array slicing/sub-array manipulation.
Visualization
Original Array (N=7, K=3 for right rotation):
+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+
Step 1: Reverse the entire array
+---+---+---+---+---+---+---+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 |
+---+---+---+---+---+---+---+
Step 2: Reverse the first K (3) elements
+---+---+---+---+---+---+---+
| 5 | 6 | 7 | 4 | 3 | 2 | 1 |
+---+---+---+---+---+---+---+
^ ^ (reversed portion)
Step 3: Reverse the remaining N-K (4) elements
+---+---+---+---+---+---+---+
| 5 | 6 | 7 | 1 | 2 | 3 | 4 |
+---+---+---+---+---+---+---+
^ ^ (reversed portion)
Final Rotated Array:
Dry Run
Let's trace the Reversal Algorithm for right rotation with nums = [1, 2, 3, 4, 5, 6, 7] and k = 3.
-
Input:
nums = [1, 2, 3, 4, 5, 6, 7],k = 3n = len(nums) = 7- Normalize
k:k = k % n = 3 % 7 = 3(No change here, but crucial ifkwas larger).
-
Reverse the entire array
nums(from index 0 ton-1):- Original:
[1, 2, 3, 4, 5, 6, 7] - After reversal:
[7, 6, 5, 4, 3, 2, 1]
- Original:
-
Reverse the first
kelements (from index 0 tok-1):k = 3, so reversenums[0:3]([7, 6, 5])- Current
nums:[7, 6, 5, 4, 3, 2, 1] - Reverse
[7, 6, 5]to[5, 6, 7] - Array becomes:
[5, 6, 7, 4, 3, 2, 1]
-
Reverse the remaining
n-kelements (from indexkton-1):k = 3,n-1 = 6, so reversenums[3:7]([4, 3, 2, 1])- Current
nums:[5, 6, 7, 4, 3, 2, 1] - Reverse
[4, 3, 2, 1]to[1, 2, 3, 4] - Array becomes:
[5, 6, 7, 1, 2, 3, 4]
-
Final Result:
[5, 6, 7, 1, 2, 3, 4](The array has been successfully rotated right by 3 positions).
Algorithm
- Given an array
numsof sizenand an integerkrepresenting the number of right rotations. - Normalize
kto ensure it's within the array bounds:k = k % n. Ifkis 0 orn, no rotation is needed; return. - Reverse the entire array
numsfrom index 0 ton-1. - Reverse the first
kelements ofnums(from index 0 tok-1). - Reverse the remaining
n-kelements ofnums(from indexkton-1).
Pseudocode
FUNCTION rotate(nums, k):
n = LENGTH(nums)
IF n IS 0 THEN RETURN
k = k MOD n
IF k IS 0 THEN RETURN
// Helper function to reverse a sub-array
FUNCTION reverse(arr, start, end):
WHILE start < end:
SWAP arr[start] AND arr[end]
start = start + 1
end = end - 1
// Step 1: Reverse the entire array
reverse(nums, 0, n - 1)
// Step 2: Reverse the first k elements
reverse(nums, 0, k - 1)
// Step 3: Reverse the remaining n-k elements
reverse(nums, k, n - 1)
Code
(See Implementations block below)
Line-by-Line Explanation
- Line 5: Gets the length of the input array
numsand stores it inn. - Line 6: Handles edge cases: if the array is empty (
n == 0) or no rotation is requested (k == 0), there's nothing to do, so the function returns early. - Line 9: Normalizes
kusing the modulo operator. Ifkis greater thann, it means we'd perform full cycles and end up at the same relative position.k % ngives the effective number of rotations needed. For example, rotating an array of size 7 by 10 positions is equivalent to rotating by 3 positions (10 % 7 = 3). - Line 10: After normalization, if
kbecomes 0 (e.g.,kwas originally equal ton,2n, etc.), it means no effective rotation is needed, so return. - Line 12: Defines a helper function
reversethat takes an array, astartindex, and anendindex. This function reverses the elements within the specified sub-array in-place. - Line 13: The
whileloop continues as long asstartis less thanend, ensuring we swap elements from both ends inwards until they meet or cross. - Line 14: Swaps the elements at
arr[start]andarr[end]using Python's tuple assignment, which is an efficient way to exchange values. - Line 15: Increments
startto move towards the center from the left. - Line 16: Decrements
endto move towards the center from the right. - Line 19: Calls the
reversehelper to reverse the entire array from index 0 ton-1. This brings the elements that should end up at the beginning to the front in reverse order, and vice-versa. - Line 22: Calls
reverseon the firstkelements (from index 0 tok-1). This corrects the order of the elements that have now moved to the beginning and belong there. - Line 25: Calls
reverseon the remainingn-kelements (from indexkton-1). This corrects the order of the elements that have shifted to the later part of the array, completing the rotation.
Time Complexity
O(N) - The Reversal Algorithm involves three distinct passes over parts of the array. Each reverse operation iterates through approximately half of the specified sub-array. In total, the sum of elements processed across these three reversals is proportional to N (the full array for the first reversal, then k elements and N-k elements for the next two, which sum up to N). Therefore, the time complexity is linear, O(N).
Space Complexity
O(1) - The Reversal Algorithm performs all operations in-place, meaning it modifies the original array directly without requiring any additional data structures whose size scales with the input array size. Only a few temporary variables are used for swapping elements, leading to constant space complexity, O(1).
Advantages
- Optimal Time Complexity: Achieves O(N) time, which is the best possible as every element must be visited at least once.
- Optimal Space Complexity: Achieves O(1) space, making it highly memory-efficient and suitable for large datasets.
- In-Place Operation: Modifies the array directly, avoiding the need for a new array and reducing memory overhead.
- Simplicity: Once understood, the logic is straightforward and elegant, requiring minimal code.
Limitations
- Mutates Original Array: Since it's an in-place algorithm, the original array is modified. If the original order is needed later, a copy must be made beforehand.
- Slightly Less Intuitive for Beginners: The 'three reversals' approach might not be immediately obvious compared to brute-force methods.
Common Mistakes
- Incorrect Modulo Operation for
k: Forgettingk %= ncan lead toIndexErrorifkis larger thannor unnecessary computations. - Off-by-One Errors in Reverse Sub-array Indices: Incorrectly setting
startandendindices for thereversehelper can lead to partial rotations orIndexError. - Not Handling
k=0ork=n: Whilek %= ngenerally handlesk=n(making it0), explicitly checking fork=0after modulo can prevent unnecessary work. - Incorrect Direction for Left vs. Right Rotation: The reversal pattern needs adjustment for left rotation (reverse first
k, reverse remainingn-k, then reverse whole array).
Edge Cases
- Empty Array:
nums = []. The code should handle this gracefully by returning early. - Array with One Element:
nums = [1]. Rotation by anykshould result in the same array. k = 0: No rotation should occur.k = n(array length): Rotation bynpositions effectively brings the array back to its original state. Thek % nnormalization handles this.k > n:kis larger than the array size.k % ncorrectly reduceskto its effective rotations.- Negative
k(for some problem variations): Though not standard for LeetCode's 'Rotate Array', some problems might imply negativekfor left rotation. The current solution assumes non-negativekfor right rotation.
Recognition Tricks
- Cyclic Shifts: Any problem involving elements moving in a circular fashion or 'wrapping around' an array's ends often hints at array rotation.
- Reordering Elements: If elements maintain their relative order but their 'starting point' shifts, think rotation.
- Prefix/Suffix Swaps: If a problem can be decomposed into swapping a prefix with a suffix (or vice-versa), reversal techniques might apply, which is the core of array rotation.
- Circular Arrays/Buffers: Explicit mentions of circular data structures directly point to rotation concepts.
Alternative Approaches
- Brute Force (Using Temporary Array): Create a new array, copy the last
kelements, then copy the firstn-kelements.O(N)time,O(N)space. - Brute Force (One by One Rotation): Rotate by one position
ktimes. In each step, move the last element to the first, and shift all others right.O(N*k)time,O(1)space. Inefficient for largek. - Juggling Algorithm (GCD-based): Based on swapping elements in cycles. Elements are moved in sets of
GCD(n, k)cycles.O(N)time,O(1)space. More complex to implement than reversal.
Optimization Tips
- Normalize
kEarly: Always performk %= nat the beginning to handle excessive rotations andk = ncases efficiently. - Handle Trivial Cases: Immediately return if
n == 0ork == 0(after normalization), as no work is needed. - In-Place Operations: Prioritize in-place algorithms like the Reversal or Juggling Algorithm to achieve
O(1)space complexity, crucial for interview optimization requirements. - Library Functions (if allowed): In some contexts, using built-in
std::rotate(C++) or array manipulation methods (Python slicing) can save development time, but interviewers usually want the manual implementation.
Interview Perspective
Interviewers frequently use array rotation problems to assess a candidate's understanding of array manipulation, in-place algorithms, modular arithmetic, and edge case handling. They look for solutions that are not just correct but also optimal in terms of time and space complexity. Expect follow-up questions like handling negative k, generalizing to left rotation, or applying rotation concepts to linked lists or 2D matrices. Demonstrating the Reversal Algorithm and explaining its efficiency is a strong indicator of problem-solving maturity and optimization skills.
Practice Problems
(See Practice Problems index below)
Implementations
class Solution:
def rotate(self, nums: list[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
if n == 0 or k == 0:
return
k %= n
if k == 0:
return
def reverse(arr, start, end):
while start < end:
arr[start], arr[end] = arr[end], arr[start]
start += 1
end -= 1
reverse(nums, 0, n - 1)
reverse(nums, 0, k - 1)
reverse(nums, k, n - 1)Practice Problems
| LeetCode Problem | ID | Difficulty | Pattern | Official Link | Leet Vision Solution |
|---|---|---|---|---|---|
| Rotate Array | rotate-array | Medium | Array Manipulation, Two Pointers | Solve on LeetCode ↗ | Video Solution ↗ |
| Rotate List | rotate-list | Medium | Linked List, Two Pointers | Solve on LeetCode ↗ | Video Solution ↗ |
| Maximum Sum Circular Subarray | maximum-sum-circular-subarray | Medium | Dynamic Programming, Array Manipulation | Solve on LeetCode ↗ | Video Solution ↗ |