Topics › Linked List › Hard Problems of LL
Rotate a LL
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given the head of a singly linked list and a non-negative integer k. Rotate the list to the right by k places: in each single rotation the last node is moved to the front. Return the head of the rotated list. k may be much larger than the length of the list.
Constraints: 0 ≤ n ≤ 500, 0 ≤ k ≤ 2 × 10^9, values fit in an int.
Example 1
head = 1 -> 2 -> 3 -> 4 -> 5, k = 2
answer = 4 -> 5 -> 1 -> 2 -> 3 // the last two nodes move to the front, in their original order
Example 2
head = 0 -> 1 -> 2, k = 4
answer = 2 -> 0 -> 1 // 4 rotations of a 3-node list equal 1 rotation
Example 3
head = 1 -> 2 -> 3, k = 3
answer = 1 -> 2 -> 3 // a full turn changes nothing
Approach 1: Brute force, rotate one step at a time
Intuition. Do exactly what the statement says, k times: find the last node, detach it from the second-to-last node, and put it in front of the head. Each step is a full walk of the list because a singly linked list gives no quick way to reach its tail.
Algorithm.
- If the list has fewer than two nodes, return it.
- Repeat
ktimes:- Walk
secondLastuntilsecondLast.next.next == null. - Let
last = secondLast.next; setsecondLast.next = null,last.next = head,head = last.
- Walk
- Return
head.
static ListNode bruteForce(ListNode head, int k) {
if (head == null || head.next == null) return head;
for (int i = 0; i < k; i++) {
ListNode secondLast = head;
while (secondLast.next.next != null) secondLast = secondLast.next;
ListNode last = secondLast.next;
secondLast.next = null; // detach the tail node
last.next = head; // and put it in front
head = last;
}
return head;
}
Complexity. Time O(n × k): k rotations, each a linear walk. Space O(1).
With k up to 2 × 10^9 this is hopeless even for a tiny list. The waste is obvious once you notice two facts. First, rotating by n returns the list to its original state, so only k mod n rotations matter. Second, those k mod n rotations together move one contiguous suffix of the list to the front; we should move the whole suffix at once instead of one node at a time.
Approach 2: Better, shift the values through an array
Intuition. Reduce k to shift = k mod n. Rotating right by shift means the node at position i in the result holds the value that was at position (i - shift) mod n in the original. Copy the values into an array, then walk the list once more writing the shifted values back. The nodes stay where they are; only their values move.
Algorithm.
- If the list is empty, return
null. - Copy the values into
vals; letnbe the size andshift = k % n. - For each position
ifrom0ton - 1, writevals[(i - shift + n) % n]into thei-th node. - Return
head.
static ListNode better(ListNode head, int k) {
if (head == null) return null;
List<Integer> vals = new ArrayList<>();
for (ListNode t = head; t != null; t = t.next) vals.add(t.val);
int n = vals.size(), shift = k % n;
ListNode t = head;
for (int i = 0; i < n; i++) {
t.val = vals.get((i - shift + n) % n); // position i of the result holds position i - shift of the original
t = t.next;
}
return head;
}
Complexity. Time O(n): two passes over the list. Space O(n) for the copied values.
The + n before % n matters: in Java, (i - shift) % n is negative when i < shift, and a negative index would throw. Adding n first keeps the operand in [0, 2n), so the remainder is a valid index.
This removes the dependence on k but still uses linear extra memory, and it moves values instead of nodes. The expected solution does the rotation with pointers alone.
Approach 3: Optimal, close the ring and cut it
Intuition. A right rotation by shift takes the last shift nodes and puts them in front. Equivalently: the node at position n - shift (1-based) becomes the new tail, the node after it becomes the new head, and the old tail must connect to the old head. The cleanest way to perform all three changes is to first join the tail to the head, turning the list into a ring, then walk n - shift - 1 steps from the old head to reach the new tail, record newHead = newTail.next, and cut the ring there. One pass to count and find the tail, one shorter walk to the cut point.
Algorithm.
- If the list has fewer than two nodes, return it.
- Walk to the tail, counting
nnodes. - Compute
shift = k % n. Ifshift == 0, returnheadunchanged. - Set
tail.next = headto close the ring. - Starting at
head, moven - shift - 1times to reachnewTail(the(n - shift)-th node). - Set
newHead = newTail.next, thennewTail.next = null. - Return
newHead.
static ListNode optimal(ListNode head, int k) {
if (head == null || head.next == null) return head;
int n = 1;
ListNode tail = head;
while (tail.next != null) { tail = tail.next; n++; }
int shift = k % n;
if (shift == 0) return head; // whole turns change nothing
tail.next = head; // close the ring
ListNode newTail = head;
for (int i = 1; i < n - shift; i++) newTail = newTail.next; // node number n - shift (1-based) becomes the new tail
ListNode newHead = newTail.next;
newTail.next = null; // open the ring again
return newHead;
}
Dry run on 1 -> 2 -> 3 -> 4 -> 5, k = 2:
| step | state |
|---|---|
| count | n = 5, tail = 5 |
| shift | 2 % 5 = 2 |
| close ring | 5.next = 1 |
| walk n - shift - 1 = 2 steps from 1 | newTail = 3 |
| newHead | 3.next = 4 |
| cut | 3.next = null |
Result: 4 -> 5 -> 1 -> 2 -> 3.
Complexity. Time O(n): the counting walk is n steps and the second walk is fewer than n. Space O(1).
Why return early when shift == 0? With shift = 0 the new tail would be node n, which is the old tail, and newHead would be tail.next = head: the code would still produce the correct result after closing and reopening the ring. The early return just avoids pointless pointer writes and makes the “full turn” case explicit.
Why does the ring trick make the cut easy? In a straight list, moving the suffix requires three separate pointer changes on nodes that are far apart (the old tail, the new tail, and the head). Closing the ring performs the “old tail to old head” link up front, after which the rotation is just a single cut anywhere on the ring. Where to cut is determined purely by counting.
Edge cases and pitfalls
klarger thann.k = 2000000003on a 5-node list must behave likek = 3. Approaches 2 and 3 reducekmodulon; the brute force would run two billion iterations, so the test file only runs it on smallk.ka multiple ofn, including0. The list is unchanged. Make sure the code does not treatshift = 0as “rotate byn”.- Empty list. Return
nullwithout computingk % n;n = 0would divide by zero. Approach 2 guards this with an explicit check, Approach 3 by returning early for lists shorter than two nodes. - Single node. Any rotation leaves it unchanged. The
head.next == nullguard also protects the brute force, whosesecondLast.next.nextwould otherwise dereferencenull. - Two nodes,
k = 1. The nodes swap. This exercises then - shift - 1 = 0case where the new tail is the head itself. - Forgetting to cut the ring. Leaving
newTail.nextpointing atnewHeadproduces a circular list and an infinite loop in the caller. - Left versus right rotation. This problem rotates right. A left rotation by
kis a right rotation byn - (k mod n); be sure which one the judge wants.
Related problems
- Reverse LL in group of given size K is another cut-and-reconnect rearrangement with constant extra space.
- Find the length of the Linked List is the counting walk that Approach 3 starts with.
- Remove Nth node from the back of the LL also locates a node by its distance from the tail.
- Rotate a LL is the SDE sheet entry for the same problem.