Problem overview
The method rotate(int[] nums, int k) shifts the array k places to the right, in place. One rotation moves every number one position right and brings the last number to the front. The method returns nothing: the caller sees the change because a Java array is passed by reference, so nums is the caller's own array.
With nums = [1, 2, 3, 4, 5] and k = 2, one rotation gives [5, 1, 2, 3, 4] and the second gives [4, 5, 1, 2, 3]. In the second example, [1, 2, 3, 4, 5, 6, 7, 8] with k = 3 becomes [6, 7, 8, 1, 2, 3, 4, 5]. The statement asks for O(n) time and O(1) extra space, with 1 <= k <= nums.length.
Approach: move each element straight to its final slot
Rotating one step at a time, k times, costs O(n * k). Copying into a second array is O(n) but uses O(n) extra memory. Both break the budget.
A rotation by k has a simple rule: the element at position i ends up at position (i + k) % n. The modulo wraps positions past the end back to the front. So each element can go directly to its destination. The only problem is that the destination is occupied. If you overwrite it, you lose the value that was there.
The fix is to carry one value in your hand. Take the element at position 0 and put it at (0 + k) % n, picking up the element that was there. That element belongs k places further on, so put it there and pick up the next one, and so on. The hand is just slot startIndex of the array itself, and a swap does both jobs at once: it drops the carried value into its destination and picks up the displaced one. No extra array is needed.
The chain of positions 0, k, 2k, and so on (all modulo n) eventually returns to its start. That loop is a cycle. When the cycle closes, the carried value belongs exactly where it is, and the swap with itself changes nothing.
Here is [1, 2, 3, 4, 5] with k = 2. The cycle visits 0, 2, 4, 1, 3 and returns to 0, so one cycle covers every position:
[3, 2, 1, 4, 5] index = 2
[5, 2, 1, 4, 3] index = 4
[2, 5, 1, 4, 3] index = 1
[4, 5, 1, 2, 3] index = 3
[4, 5, 1, 2, 3] index = 0, cycle closed
When n and k share a factor, one cycle is not enough. With n = 6 and k = 2, the chain 0, 2, 4 returns to 0 after three steps and never touches positions 1, 3 and 5. They form a second cycle, which needs its own starting point. The array [1, 2, 3, 4, 5, 6] goes through these states:
[3, 2, 1, 4, 5, 6] index = 2, start = 0
[5, 2, 1, 4, 3, 6] index = 4, start = 0
[5, 2, 1, 4, 3, 6] index = 0, start = 0 (cycle closed, move start to 1)
[5, 4, 1, 2, 3, 6] index = 3, start = 1
[5, 6, 1, 2, 3, 4] index = 5, start = 1
[5, 6, 1, 2, 3, 4] index = 1, start = 1 (cycle closed)
Every swap puts one more element into its final slot, and the self-swap that closes a cycle finishes the carried one. So exactly n swaps are needed in total, whatever the number of cycles. That is why the loop simply runs n times, with no cycle counting and no gcd computation.
Step by step
- Declare the state.
nis the length,indexis the position of the element about to move, andstartIndexis the first slot of the current cycle, which holds the carried value. - Loop exactly
ntimes. Each pass fixes one element, so this is the whole job. The variableconly counts. - Find the destination as
nextIndex = (index + k) % n, wrapping past the end. - Swap
nums[nextIndex]withnums[startIndex]throughtmp. The carried value lands in its slot, and the old occupant becomes the new carried value. - Move on by setting
indextonextIndex. The displaced element now sits in the hand, and its own destination iskplaces afterindex. - Detect a closed cycle. When
indexis back atstartIndex, move both to the next slot, so the following passes start a fresh cycle. If there was only one cycle, the loop ends right after this anyway.
The solution in Java
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length, index = 0, startIndex = 0;
for (int c = 0; c < n; c++) {
int nextIndex = (index + k) % n;
int tmp = nums[nextIndex];
nums[nextIndex] = nums[startIndex];
nums[startIndex] = tmp;
index = nextIndex;
if (index == startIndex) {
startIndex++; index++;
}
}
}
}
The Java details worth a look:
- In place means the caller's array. The method receives a copy of the reference, not a copy of the array. Writing into
nums[...]changes the array the caller holds, which is whyvoidis enough. - One declaration, three variables.
int n = nums.length, index = 0, startIndex = 0;declares threeintvariables in one statement. Each has its own initializer. - The swap needs a temporary. Java has no tuple assignment, so
tmpholds one value while the other is copied over. It is declared inside the loop, so it exists only during one pass. %andintrange.index + kis at most about twice the array length, far from theintlimit, and both operands are non-negative, so%never gives a negative result here.startIndex++; index++;on one line is two statements. Both variables must move together, or the next cycle would start from the wrong slot.
Complexity
Time: O(n). The loop runs n times, and each pass does a constant amount of work: one modulo, one swap, one comparison.
Space: O(1). The method uses a handful of int variables whatever the size of nums. The carried value lives inside the array.
Edge cases and common mistakes
- A single element. With
n = 1, the destination is the element's own slot, the cycle closes at once and the array stays the same. kequal to the length. A full turn changes nothing. HerenextIndexalways equalsindex, so every pass is a self-swap that closes a cycle, andstartIndexwalks across the array. The array is unchanged.- Shared factors.
[1, 2, 3, 4, 5, 6]withk = 2has two cycles. A version without thestartIndexrestart would never reach positions 1, 3 and 5, so the array would come out wrong. - Stopping when
index == 0. That ends the first cycle only. Counting passes up tonis what covers every cycle. - Rotating the wrong way. The destination is
(index + k) % n. Usingindex - kwould rotate left, and could go negative, because Java's%keeps the sign of the dividend. - Larger
k. The statement keepskat most the array length. Because of the modulo,[1, 2, 3, 4, 5, 6]withk = 8also gives[5, 6, 1, 2, 3, 4], the same ask = 2.
Testing
[1, 2, 3, 4, 5],k = 2gives[4, 5, 1, 2, 3].[1, 2, 3, 4, 5, 6, 7, 8],k = 3gives[6, 7, 8, 1, 2, 3, 4, 5].[1, 2, 3, 4, 5, 6],k = 2gives[5, 6, 1, 2, 3, 4].[1, 2, 3, 4, 5, 6],k = 6gives[1, 2, 3, 4, 5, 6].[7],k = 1gives[7].
