189. Rotate Array
Quick Identifier
- Topic: arrays-hashing
- Pattern: The Triple Reverse Trick
- Hint: Don't try to shift elements one by one, and don't try to map indices in an $O(1)$ cyclic replacement (it's too hard to code without bugs). Instead, just reverse the whole array, then reverse the two halves independently.
- Difficulty: Medium
- Companies: Amazon, Microsoft, Apple, Google, Meta
- LeetCode: 189
Quick Sights
- Time Complexity:
O(n). Reversing the entire array takes $O(n)$. Reversing the two halves takes another $O(n)$. Total time is $O(2n)$, which simplifies to $O(n)$. - Space Complexity:
O(1). The problem explicitly asks for an in-place modification. - Core Mechanism:
- Normalize
k(because rotating an array of length 5 by 6 steps is the same as rotating by 1 step).k = k % n. - Reverse the entire array from
0ton - 1. - Reverse the first
kelements (from0tok - 1). - Reverse the remaining
n - kelements (fromkton - 1).
- Normalize
Concept Explanation
As a senior engineer, the "Triple Reverse" trick is a piece of algorithmic trivia that you simply must know. Trying to derive it from first principles during an interview is unnecessary.
- The Brute Force Danger: If you try to literally "pop" an element off the back and "unshift" it to the front
ktimes, you are doing an $O(n)$ operation $k$ times. This is $O(n \cdot k)$ and will time out. - The Cyclic Replacement Nightmare: The mathematical alternative is to jump indices:
temp = nums[i],nums[(i+k)%n] = temp. But what happens ifkandnshare a greatest common denominator? You end up in infinite loops and have to trackvisitedelements. It is an absolute nightmare to write bug-free on a whiteboard. - The Elegant Geometric Solution: Look at an array as two blocks: Block A (the part that will stay in the front but shift right) and Block B (the part that will wrap around to the very front).
- Original:
[ Block A | Block B ] - Goal:
[ Block B | Block A ] - Reverse the whole thing:
[ Reversed B | Reversed A ] - Reverse the chunks independently:
[ Block B | Block A ]. Magic.
- Original:
Diagram
This flowchart outlines the three distinct reversal phases.
Loading diagram…
Study Pattern (SDE3+)
- 0–3 min: Immediately name the "Triple Reverse" method. Explain why Cyclic Replacement is dangerous to code under pressure, and why $O(n \cdot k)$ element-shifting is a failure.
- Implementation pass: Write a modular
reverse(nums, start, end)helper function. Do not write thewhile (start < end)logic three separate times in your main function. It looks amateurish. - 5 min extension: What if the interviewer asks you to rotate the array left instead of right? The algorithm is identical, but the boundaries change. For a left rotation of
k, you reverse0tok-1, thenkton-1, and then reverse the whole array. (Alternatively, rotating left bykis identical to rotating right byn - k).
Approaches
- Shift One-by-One — Move the last element to the front,
ktimes. Time: $O(n \cdot k)$, Space: $O(1)$. TLE (Time Limit Exceeded). - Extra Array / Slice — Cut the array into two pieces and paste them together into a new array. Time: $O(n)$, Space: $O(n)$. Fails the $O(1)$ space constraint.
- Triple Reverse — Time: $O(n)$, Space: $O(1)$. (Always pick this)
Optimal Solution
Go
func rotate(nums []int, k int) {
n := len(nums)
if n == 0 {
return
}
// Normalize k to prevent unnecessary rotations
k = k % n
// Phase 1: Reverse the entire array
reverse(nums, 0, n-1)
// Phase 2: Reverse the first k elements
reverse(nums, 0, k-1)
// Phase 3: Reverse the remaining n - k elements
reverse(nums, k, n-1)
}
// Helper function to reverse a segment of the array in-place
func reverse(nums []int, start int, end int) {
for start < end {
nums[start], nums[end] = nums[end], nums[start]
start++
end--
}
}JavaScript
function rotate(nums, k) {
const n = nums.length;
if (n === 0) return;
// Normalize k
k = k % n;
// Apply the triple reverse
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
function reverse(nums, start, end) {
while (start < end) {
// Swap elements
const temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}Walkthrough
Input: nums = [1, 2, 3, 4, 5, 6, 7], k = 3
Length n = 7. k % n = 3.
| Phase | Operation | nums State | Concept |
|---|---|---|---|
| Initial | - | [1, 2, 3, 4, 5, 6, 7] | [A: 1 2 3 4] [B: 5 6 7] |
| 1 | Reverse All | [7, 6, 5, 4, 3, 2, 1] | [Rev B: 7 6 5] [Rev A: 4 3 2 1] |
| 2 | Reverse 0 to 2 | [5, 6, 7, 4, 3, 2, 1] | [B: 5 6 7] [Rev A: 4 3 2 1] |
| 3 | Reverse 3 to 6 | [5, 6, 7, 1, 2, 3, 4] | [B: 5 6 7] [A: 1 2 3 4] |
Output: [5, 6, 7, 1, 2, 3, 4]
Edge Cases
kis exactly a multiple ofn(e.g.,n = 5, k = 10).k % nbecomes0. The first reverse flips it, the second reverse is a no-op, the third reverse flips it back to the original. Correct behavior, but slightly wasteful. (You can add anif (k == 0) returnto optimize).kis larger thann. Handled cleanly byk % n.- Array length is 1. All reverses instantly terminate because
start < endis false.
Pitfalls
- Forgetting to modulo
k. Ifk = 5andn = 3, passingk-1(4) to the reverse function will cause an index out of bounds error. - Passing
kinstead ofk-1as theendindex for Phase 2. Remember, arrays are 0-indexed. Thekth element is at indexk-1.
Similar Problems
- 048. Rotate Image — Similar conceptual composition of operations (Transpose + Reverse) to achieve rotation.
- 151. Reverse Words in a String — Another string/array problem heavily utilizing the "reverse parts, then reverse whole" trick.
Mind-Map Tags
#arrays #in-place #reverse #rotation #modular-math
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?