Builds on Converging Pointers, which you haven’t finished yet.
In-Place Array Rotation (Rotate Array)
You get an integer array nums and a non-negative integer k. Shift every item k places to the right; an item pushed past the last index wraps around to the front. Change nums itself: the function returns nothing. k can be larger than the length of nums.
Follow-up: LeetCode asks for as many different solutions as you can find, and whether one of them can work in place with extra space.
Three-Reversals Algorithm
array ➔ reverse whole, then both parts ➔ rotated array| 1 | def rotate_right(nums: list[int], k: int) -> None: |
| 2 | """Rotate Array: shift right by k in place with three reversals. |
| 3 | Time: O(N) | Space: O(1) |
| 4 | """ |
| 5 | def reverse(lo: int, hi: int) -> None: |
| 6 | while lo < hi: # Swap inward from both ends |
| 7 | nums[lo], nums[hi] = nums[hi], nums[lo] |
| 8 | lo, hi = lo + 1, hi - 1 |
| 9 | |
| 10 | n = len(nums) |
| 11 | if n == 0: |
| 12 | return |
| 13 | k %= n # Rotating by n is a no-op; k >= n wraps around |
| 14 | reverse(0, n - 1) # [A | B] -> [B^R | A^R] |
| 15 | reverse(0, k - 1) # B^R -> B (the last k items, now in front) |
| 16 | reverse(k, n - 1) # A^R -> A |
reverse(0, n - 1) the last k items are in front, each block mirrored; the two block reversals only fix the order inside a block.Rotating right by k moves the last k items (B) in front of the rest (A), and reversing a sequence also reverses the order of its blocks: reverse(A B) = B^R A^R. So reverse(0, n - 1) puts the blocks in the right order, then reverse(0, k - 1) and reverse(k, n - 1) straighten each block, all in place. Do k %= n first, because k can be larger than the array.
See it run
Rotate Array, nums = [1, 2, 3, 4, 5, 6, 7], k = 10
call startk 10
The top row is nums. The bottom row names the block each value came from, so you can follow the blocks while they move.
lothe left end of the range being reversedhithe right end of the range being reversed
nums has 7 items and k = 10. After k is reduced below 7, block A is the first n - k items and block B is the last k items.
Gotcha 1 of 4
- The trap
Gotcha 1: Leaving out
k %= nMistakereverse(0, n - 1)Fixk %= n reverse(0, n - 1)What happens:
kcan be larger thann, the length ofnums. Rotating byngives the same array, so onlyk % nsteps matter. Withoutk %= n,reverse(0, k - 1)reads past the end ofnums. Python raises an IndexError.Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 10Mistake: raises IndexErrorFix: returns [5, 6, 7, 1, 2, 3, 4]
- A square grid must turn by 90 degrees, as in Rotate Image (LC 48): rows become columns, which no shift of the flattened list can do. See Spiral Matrix
- The data is a linked list, as in Reverse Linked List (LC 206), so there are no indices to swap from both ends. See Linked List Reversal
Deep Dive
Why three reversals rotate the array
Rotating right by k moves the last k items to the front. Call the first n - k items block A and the last k items block B. The array is A B, and the rotated array is B A.
Reversing a whole sequence reads it from the end. So reverse(A B) gives the items of B first, in reversed order, then the items of A, in reversed order. Write this as B^R A^R. The blocks are now in the right places, but each block is mirrored.
Two more reversals fix the mirror. reverse(0, k - 1) turns B^R back into B. reverse(k, n - 1) turns A^R back into A. The array is B A.
Trace nums = [1, 2, 3, 4, 5, 6, 7] with k = 10. After k %= n, k = 3, so A = [1, 2, 3, 4] and B = [5, 6, 7]. reverse(0, 6) gives [7, 6, 5, 4, 3, 2, 1]. reverse(0, 2) gives [5, 6, 7, 4, 3, 2, 1]. reverse(3, 6) gives [5, 6, 7, 1, 2, 3, 4]. Rotate Array (LC 189) gives the same answer for k = 3, the value in its first example.
Each reverse is the two-pointer swap from the Squeeze Ends card: lo and hi move inward and swap until they meet. This card adds only the choice of the three ranges.
Why k %= n comes first
Rotating by n moves every item all the way around, so the array is the same as before. Rotating by k and rotating by k % n give the same array.
Without k %= n, a k larger than n breaks the code. For n = 7 and k = 10, reverse(0, k - 1) is reverse(0, 9), and nums[9] raises an IndexError.
The check if n == 0: return comes before k %= n. In Python, k %= 0 raises a ZeroDivisionError.
When k % n is 0, reverse(0, -1) has lo >= hi at once and does nothing. The other two reversals then undo each other, because the second reverses the whole array again. The array stays the same.
The order of the reversals and the in-place rule
The whole array must be reversed first. If the two parts are reversed first and the whole array last, the result is a rotation to the left by k. For nums = [1, 2, 3, 4, 5, 6, 7] and k = 3, the parts give [3, 2, 1, 7, 6, 5, 4], and the whole reversal gives [4, 5, 6, 7, 1, 2, 3]. This is the correct order for a left rotation. A right rotation by k is the same as a left rotation by n - k.
The function must change the list that the caller passed. The line nums = nums[-k:] + nums[:-k] builds a new list and points the local name nums at it. The caller's list does not change, and the new list also uses O(N) extra space. The swaps nums[lo], nums[hi] = nums[hi], nums[lo] change the original list.
Shift 2D Grid (LC 1260) moves the last value of each row to the start of the next row. Read the grid as one list, row by row. Index i of that list is the cell (i // cols, i % cols). Rotate the list, then write it back. Rotating each row on its own is wrong.
Cost and related problems
The three reversals swap about n / 2 + k / 2 + (n - k) / 2 pairs, which is about n swaps. So the time is O(N) and the space is O(1). For the board, the reversals use 3, 1 and 2 swaps, which is 6 swaps for 7 items.
A copy into a new list is O(N) time and O(N) space. Moving each item to its final place, one cycle at a time, is O(N) time and O(1) space, but it needs the gcd of n and k to count the cycles. The three reversals are shorter to write.
Reverse Words in a String (LC 151) uses the same idea in another shape: reverse the whole text, then reverse each word. The in-place version is Reverse Words in a String II (LC 186).
Rotate List (LC 61) rotates a linked list. A linked list has no indices to swap from both ends, so find the new tail and change the links instead. Rotate Image (LC 48) turns a square grid by 90 degrees. Rows become columns, which no shift of one flat list can do.
60-Second Pitch
"Rotating right by k turns A B into B A, where B is the last k items. Reversing a sequence also reverses the order of its blocks, so reverse(A B) is B^R A^R. I first do k %= n, because k can be larger than the array. Then I reverse the whole array. Then I reverse the first k items, and then the rest. Each block is back in its own order, and the blocks are in the right places. Each reversal swaps pairs from both ends in place. The time is O(N) and the space is O(1)."
Common Questions
- What is the time and space complexity?
O(N)time, because the three reversals make aboutnswaps in total.O(1)extra space, because the swaps happen insidenums.- Why does reversing the whole array first work?
Reversing
A BgivesB^R A^R. The lastkitems are now in front, and only the order inside each block is wrong. The two reversals of the blocks fix that.- How do you rotate to the left instead?
Rotate right by
n - k, or reverse the two parts first and the whole array last. For[1, 2, 3, 4, 5, 6, 7]andk = 3, the left rotation is[4, 5, 6, 7, 1, 2, 3].- Why does the code need k %= n?
Rotating by
ngives the same array, so onlyk % nmatters. Ifkis larger thann,reverse(0, k - 1)reads past the end ofnumsand raises an IndexError.- Is there another
O(1)space method? Yes. You can move each item straight to its final index,
(i + k) % n, following each cycle until it closes. The number of cycles is the gcd ofnandk. It is alsoO(N)time, but it is harder to write without a mistake.
SWE Reasoning
Spot it
- The statement asks to rotate or cyclically shift an array by
kpositions, in place (Rotate Array (LC 189)). - A grid can be read as one flat list, and the shift moves values across row ends (Shift 2D Grid (LC 1260)).
- Extra space must be
O(1), so a second array is not allowed. - A text must change the order of its blocks and keep the order inside each block (Reverse Words in a String (LC 151)).
The invariant, step by step
- The array is
A B, whereBis the lastkitems. The goal isB A. reverse(0, n - 1)givesB^R A^R. The lastkitems are in front, and each block is mirrored.reverse(0, k - 1)reverses the firstkitems, soB^RbecomesB.reverse(k, n - 1)reverses the rest, soA^RbecomesA.- The array is
B A. Eachreverseswaps pairs from both ends, so it changesnumsin place and takesO(1)extra space.
Traps
- No
k %= n. Fork = 10and 7 items,reverse(0, 9)raises an IndexError. - Reversing the parts first. The result is a left rotation:
[4, 5, 6, 7, 1, 2, 3]instead of[5, 6, 7, 1, 2, 3, 4]. nums = nums[-k:] + nums[:-k]. The caller's list does not change, and the new list costsO(N)space.- No check for an empty array.
k %= nwithn = 0raises a ZeroDivisionError. - Rotating each row of a grid. Values must move from the end of one row to the start of the next. Rotate the flattened list.
Practice
Builds on Converging Pointers, which you haven’t finished yet.
In-Place Array Rotation (Rotate Array)
You get an integer array nums and a non-negative integer k. Shift every item k places to the right; an item pushed past the last index wraps around to the front. Change nums itself: the function returns nothing. k can be larger than the length of nums.
Follow-up: LeetCode asks for as many different solutions as you can find, and whether one of them can work in place with extra space.
Three-Reversals Algorithm
array ➔ reverse whole, then both parts ➔ rotated array| 1 | def rotate_right(nums: list[int], k: int) -> None: |
| 2 | """Rotate Array: shift right by k in place with three reversals. |
| 3 | Time: O(N) | Space: O(1) |
| 4 | """ |
| 5 | def reverse(lo: int, hi: int) -> None: |
| 6 | while lo < hi: # Swap inward from both ends |
| 7 | nums[lo], nums[hi] = nums[hi], nums[lo] |
| 8 | lo, hi = lo + 1, hi - 1 |
| 9 | |
| 10 | n = len(nums) |
| 11 | if n == 0: |
| 12 | return |
| 13 | k %= n # Rotating by n is a no-op; k >= n wraps around |
| 14 | reverse(0, n - 1) # [A | B] -> [B^R | A^R] |
| 15 | reverse(0, k - 1) # B^R -> B (the last k items, now in front) |
| 16 | reverse(k, n - 1) # A^R -> A |
reverse(0, n - 1) the last k items are in front, each block mirrored; the two block reversals only fix the order inside a block.Rotating right by k moves the last k items (B) in front of the rest (A), and reversing a sequence also reverses the order of its blocks: reverse(A B) = B^R A^R. So reverse(0, n - 1) puts the blocks in the right order, then reverse(0, k - 1) and reverse(k, n - 1) straighten each block, all in place. Do k %= n first, because k can be larger than the array.
See it run
Rotate Array, nums = [1, 2, 3, 4, 5, 6, 7], k = 10
call startk 10
The top row is nums. The bottom row names the block each value came from, so you can follow the blocks while they move.
lothe left end of the range being reversedhithe right end of the range being reversed
nums has 7 items and k = 10. After k is reduced below 7, block A is the first n - k items and block B is the last k items.
Gotcha 1 of 4
- The trap
Gotcha 1: Leaving out
k %= nMistakereverse(0, n - 1)Fixk %= n reverse(0, n - 1)What happens:
kcan be larger thann, the length ofnums. Rotating byngives the same array, so onlyk % nsteps matter. Withoutk %= n,reverse(0, k - 1)reads past the end ofnums. Python raises an IndexError.Example: nums = [1, 2, 3, 4, 5, 6, 7], k = 10Mistake: raises IndexErrorFix: returns [5, 6, 7, 1, 2, 3, 4]
- A square grid must turn by 90 degrees, as in Rotate Image (LC 48): rows become columns, which no shift of the flattened list can do. See Spiral Matrix
- The data is a linked list, as in Reverse Linked List (LC 206), so there are no indices to swap from both ends. See Linked List Reversal
Deep Dive
Why three reversals rotate the array
Rotating right by k moves the last k items to the front. Call the first n - k items block A and the last k items block B. The array is A B, and the rotated array is B A.
Reversing a whole sequence reads it from the end. So reverse(A B) gives the items of B first, in reversed order, then the items of A, in reversed order. Write this as B^R A^R. The blocks are now in the right places, but each block is mirrored.
Two more reversals fix the mirror. reverse(0, k - 1) turns B^R back into B. reverse(k, n - 1) turns A^R back into A. The array is B A.
Trace nums = [1, 2, 3, 4, 5, 6, 7] with k = 10. After k %= n, k = 3, so A = [1, 2, 3, 4] and B = [5, 6, 7]. reverse(0, 6) gives [7, 6, 5, 4, 3, 2, 1]. reverse(0, 2) gives [5, 6, 7, 4, 3, 2, 1]. reverse(3, 6) gives [5, 6, 7, 1, 2, 3, 4]. Rotate Array (LC 189) gives the same answer for k = 3, the value in its first example.
Each reverse is the two-pointer swap from the Squeeze Ends card: lo and hi move inward and swap until they meet. This card adds only the choice of the three ranges.
Why k %= n comes first
Rotating by n moves every item all the way around, so the array is the same as before. Rotating by k and rotating by k % n give the same array.
Without k %= n, a k larger than n breaks the code. For n = 7 and k = 10, reverse(0, k - 1) is reverse(0, 9), and nums[9] raises an IndexError.
The check if n == 0: return comes before k %= n. In Python, k %= 0 raises a ZeroDivisionError.
When k % n is 0, reverse(0, -1) has lo >= hi at once and does nothing. The other two reversals then undo each other, because the second reverses the whole array again. The array stays the same.
The order of the reversals and the in-place rule
The whole array must be reversed first. If the two parts are reversed first and the whole array last, the result is a rotation to the left by k. For nums = [1, 2, 3, 4, 5, 6, 7] and k = 3, the parts give [3, 2, 1, 7, 6, 5, 4], and the whole reversal gives [4, 5, 6, 7, 1, 2, 3]. This is the correct order for a left rotation. A right rotation by k is the same as a left rotation by n - k.
The function must change the list that the caller passed. The line nums = nums[-k:] + nums[:-k] builds a new list and points the local name nums at it. The caller's list does not change, and the new list also uses O(N) extra space. The swaps nums[lo], nums[hi] = nums[hi], nums[lo] change the original list.
Shift 2D Grid (LC 1260) moves the last value of each row to the start of the next row. Read the grid as one list, row by row. Index i of that list is the cell (i // cols, i % cols). Rotate the list, then write it back. Rotating each row on its own is wrong.
Cost and related problems
The three reversals swap about n / 2 + k / 2 + (n - k) / 2 pairs, which is about n swaps. So the time is O(N) and the space is O(1). For the board, the reversals use 3, 1 and 2 swaps, which is 6 swaps for 7 items.
A copy into a new list is O(N) time and O(N) space. Moving each item to its final place, one cycle at a time, is O(N) time and O(1) space, but it needs the gcd of n and k to count the cycles. The three reversals are shorter to write.
Reverse Words in a String (LC 151) uses the same idea in another shape: reverse the whole text, then reverse each word. The in-place version is Reverse Words in a String II (LC 186).
Rotate List (LC 61) rotates a linked list. A linked list has no indices to swap from both ends, so find the new tail and change the links instead. Rotate Image (LC 48) turns a square grid by 90 degrees. Rows become columns, which no shift of one flat list can do.
60-Second Pitch
"Rotating right by k turns A B into B A, where B is the last k items. Reversing a sequence also reverses the order of its blocks, so reverse(A B) is B^R A^R. I first do k %= n, because k can be larger than the array. Then I reverse the whole array. Then I reverse the first k items, and then the rest. Each block is back in its own order, and the blocks are in the right places. Each reversal swaps pairs from both ends in place. The time is O(N) and the space is O(1)."
Common Questions
- What is the time and space complexity?
O(N)time, because the three reversals make aboutnswaps in total.O(1)extra space, because the swaps happen insidenums.- Why does reversing the whole array first work?
Reversing
A BgivesB^R A^R. The lastkitems are now in front, and only the order inside each block is wrong. The two reversals of the blocks fix that.- How do you rotate to the left instead?
Rotate right by
n - k, or reverse the two parts first and the whole array last. For[1, 2, 3, 4, 5, 6, 7]andk = 3, the left rotation is[4, 5, 6, 7, 1, 2, 3].- Why does the code need k %= n?
Rotating by
ngives the same array, so onlyk % nmatters. Ifkis larger thann,reverse(0, k - 1)reads past the end ofnumsand raises an IndexError.- Is there another
O(1)space method? Yes. You can move each item straight to its final index,
(i + k) % n, following each cycle until it closes. The number of cycles is the gcd ofnandk. It is alsoO(N)time, but it is harder to write without a mistake.
SWE Reasoning
Spot it
- The statement asks to rotate or cyclically shift an array by
kpositions, in place (Rotate Array (LC 189)). - A grid can be read as one flat list, and the shift moves values across row ends (Shift 2D Grid (LC 1260)).
- Extra space must be
O(1), so a second array is not allowed. - A text must change the order of its blocks and keep the order inside each block (Reverse Words in a String (LC 151)).
The invariant, step by step
- The array is
A B, whereBis the lastkitems. The goal isB A. reverse(0, n - 1)givesB^R A^R. The lastkitems are in front, and each block is mirrored.reverse(0, k - 1)reverses the firstkitems, soB^RbecomesB.reverse(k, n - 1)reverses the rest, soA^RbecomesA.- The array is
B A. Eachreverseswaps pairs from both ends, so it changesnumsin place and takesO(1)extra space.
Traps
- No
k %= n. Fork = 10and 7 items,reverse(0, 9)raises an IndexError. - Reversing the parts first. The result is a left rotation:
[4, 5, 6, 7, 1, 2, 3]instead of[5, 6, 7, 1, 2, 3, 4]. nums = nums[-k:] + nums[:-k]. The caller's list does not change, and the new list costsO(N)space.- No check for an empty array.
k %= nwithn = 0raises a ZeroDivisionError. - Rotating each row of a grid. Values must move from the end of one row to the start of the next. Rotate the flattened list.