0061. Rotate List¶
Table of Contents¶
- Problem
- Problem Breakdown
- Approach 1: Brute Force (Rotate One at a Time)
- Approach 2: Find Length and Re-Link
- Approach 3: Make Circular and Cut
- Complexity Comparison
- Edge Cases
- Common Mistakes
- Related Problems
- Visual Animation
Problem¶
| Leetcode | 61. Rotate List |
| Difficulty | |
| Tags | Linked List, Two Pointers |
Description¶
Given the
headof a linked list, rotate the list to the right bykplaces.
Examples¶
Example 1:
Input: head = [1,2,3,4,5], k = 2
Output: [4,5,1,2,3]
Example 2:
Input: head = [0,1,2], k = 4
Output: [2,0,1]
Constraints¶
- The number of nodes in the list is in the range
[0, 500]. -100 <= Node.val <= 1000 <= k <= 2 * 10^9
Problem Breakdown¶
1. What is being asked?¶
Cut the last k nodes off the end of the list and stick them at the front, repeated as a single rotation. Equivalently, find the new head at position n - k % n (0-indexed) from the front.
2. What is the input?¶
| Parameter | Type | Description |
|---|---|---|
head | ListNode | Head of the linked list (may be null) |
k | int | Number of rotations, 0 <= k <= 2*10^9 |
Important observations: - k may be much larger than n -- always reduce by k % n - If n == 0 or n == 1 or k % n == 0, no rotation is needed
3. What is the output?¶
The head of the rotated list.
4. Constraints analysis¶
| Constraint | Significance |
|---|---|
n <= 500 | O(n) is plenty fast |
k <= 2 * 10^9 | Don't loop k times directly -- mod by n first |
5. Step-by-step example analysis¶
Example 1: head = [1,2,3,4,5], k = 2¶
Length n = 5. Effective k = 2 % 5 = 2.
New tail index from front: n - k - 1 = 5 - 2 - 1 = 2 (0-based) โ node with value 3.
Steps:
- Walk to node at index 2 (value 3). Call it newTail.
- newHead = newTail.next (value 4)
- Walk to actual tail (value 5).
- Tail.next = head (value 1)
- newTail.next = null
Result: 4 -> 5 -> 1 -> 2 -> 3 -> null
6. Key Observations¶
- Modulo reduction --
k = k % nso we never rotate more than necessary. - The cut is the only mutation -- We need exactly two pointer rewrites:
tail.next = head,newTail.next = null. - Edge cases -- Empty list, single node, or
k % n == 0mean "do nothing". - Two-pass is the cleanest -- First pass finds length and tail; second pass walks to the new tail.
7. Pattern identification¶
| Pattern | Why it fits |
|---|---|
| Two passes | Length + cut |
| Make-circular-and-cut | One pass through length, then re-walk |
| Brute force step-by-step | Move last node to front k times |
Chosen pattern: Make Circular and Cut (or equivalently the two-pass version).
Approach 1: Brute Force (Rotate One at a Time)¶
Thought process¶
One rotation = unhook the last node and put it at the front. Repeat
k % ntimes.
Algorithm¶
- Compute
n. Ifn <= 1ork % n == 0, return head. - Reduce
k = k % n. - Repeat
ktimes: - Walk to the second-to-last node
prev. prev.next.next = head;head = prev.next;prev.next = null.
Complexity¶
| Complexity | |
|---|---|
| Time | O(n * k) |
| Space | O(1) |
For
kclose ton, this is O(n^2). Listed for completeness.
Approach 2: Find Length and Re-Link¶
Idea¶
Pass 1: walk the list, count
n, remember the tail. Pass 2: walk to positionn - k - 1(the new tail). Cut and reattach.
Algorithm (step-by-step)¶
- If
headorhead.nextis null, return head. - Walk from
headto countnand findtail. k %= n. Ifk == 0, return head.- Walk
n - k - 1steps from head to the new tail. newHead = newTail.next;newTail.next = null;tail.next = head.- Return
newHead.
Pseudocode¶
if head is null or head.next is null: return head
n = 1, tail = head
while tail.next is not null: tail = tail.next; n++
k = k % n
if k == 0: return head
newTail = head
for _ in 0..n-k-2: newTail = newTail.next
newHead = newTail.next
newTail.next = null
tail.next = head
return newHead
Complexity¶
| Complexity | |
|---|---|
| Time | O(n) |
| Space | O(1) |
Implementation¶
Go¶
type ListNode struct {
Val int
Next *ListNode
}
func rotateRight(head *ListNode, k int) *ListNode {
if head == nil || head.Next == nil {
return head
}
n, tail := 1, head
for tail.Next != nil {
tail = tail.Next
n++
}
k %= n
if k == 0 {
return head
}
newTail := head
for i := 0; i < n-k-1; i++ {
newTail = newTail.Next
}
newHead := newTail.Next
newTail.Next = nil
tail.Next = head
return newHead
}
Java¶
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public ListNode rotateRight(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++; }
k %= n;
if (k == 0) return head;
ListNode newTail = head;
for (int i = 0; i < n - k - 1; i++) newTail = newTail.next;
ListNode newHead = newTail.next;
newTail.next = null;
tail.next = head;
return newHead;
}
}
Python¶
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def rotateRight(self, head: 'ListNode', k: int) -> 'ListNode':
if not head or not head.next:
return head
n, tail = 1, head
while tail.next:
tail = tail.next
n += 1
k %= n
if k == 0:
return head
new_tail = head
for _ in range(n - k - 1):
new_tail = new_tail.next
new_head = new_tail.next
new_tail.next = None
tail.next = head
return new_head
Dry Run¶
head = 1 โ 2 โ 3 โ 4 โ 5, k = 2
Pass 1: tail walks to 5. n = 5.
k = 2 % 5 = 2.
Pass 2: newTail walks n - k - 1 = 2 steps:
newTail starts at 1, then 2, then 3 (after 2 steps).
newHead = newTail.next = 4.
newTail.next = null โ 1 โ 2 โ 3
tail.next = head โ 5 โ 1 โ 2 โ 3
Final: 4 โ 5 โ 1 โ 2 โ 3 โ null
Approach 3: Make Circular and Cut¶
Idea¶
Connect the tail back to the head to form a cycle, then walk forward
n - k % nsteps from the head and cut. The node where we cut becomes the new tail; its next becomes the new head.Same big-O as Approach 2; some prefer the symmetry of "make-then-cut".
Algorithm¶
- If
headis null, return null. - Compute
nand linktail.next = head(cycle). k %= n.- Walk
n - k - 1steps from head to find new tail. newHead = newTail.next;newTail.next = null.- Return
newHead.
Implementation¶
Python¶
class Solution:
def rotateRightCircular(self, head, k):
if not head: return head
n, tail = 1, head
while tail.next:
tail = tail.next; n += 1
tail.next = head # close cycle
k %= n
new_tail = head
for _ in range(n - k - 1):
new_tail = new_tail.next
new_head = new_tail.next
new_tail.next = None # cut
return new_head
Complexity Comparison¶
| # | Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|---|
| 1 | Brute Rotate One | O(n * k) | O(1) | Trivial | TLE for large k near n |
| 2 | Length + Re-Link | O(n) | O(1) | Two-pass linear | Two passes |
| 3 | Make Circular + Cut | O(n) | O(1) | Symmetric | Same as 2 essentially |
Which solution to choose?¶
- In an interview: Approach 2 -- canonical
- In production: Approach 2 or 3
- On Leetcode: Approach 2
Edge Cases¶
| # | Case | Input | Expected | Reason |
|---|---|---|---|---|
| 1 | Empty list | [], k=3 | [] | Nothing to rotate |
| 2 | Single node | [5], k=10 | [5] | One node always returns itself |
| 3 | k = 0 | [1,2,3], k=0 | [1,2,3] | No rotation |
| 4 | k = n | [1,2,3], k=3 | [1,2,3] | Full circle |
| 5 | k > n | [0,1,2], k=4 | [2,0,1] | Mod by n |
| 6 | Two nodes, k = 1 | [1,2], k=1 | [2,1] | Swap |
| 7 | k = 2 * 10^9 | [1,2,3], k=2_000_000_000 | depends on mod | Mod handles huge k |
Common Mistakes¶
Mistake 1: Rotating k times directly¶
# WRONG โ TLE when k is huge
for _ in range(k):
rotate_one(head)
# CORRECT โ reduce first
k %= n
Reason: k can be 2 * 10^9. Reducing modulo n makes the work bounded by n.
Mistake 2: Ignoring k == 0 after modulo¶
# WRONG โ when k % n == 0, we walk to the wrong newTail
newTail = head
for _ in range(n - k - 1): ... # n-k-1 = n-1, walks to actual tail
newHead = newTail.next # which is null!
# CORRECT โ early return
if k == 0: return head
Reason: When k % n == 0, the rotation is a no-op. Without the early return, the algorithm walks too far and dereferences null.
Mistake 3: Forgetting to terminate the new tail¶
# WRONG โ leaves a cycle (or dangling old tail link)
tail.next = head
return new_head
# CORRECT โ cut the new tail before reconnecting
new_tail.next = None
tail.next = head
return new_head
Reason: Without new_tail.next = None, traversing the result hits the old prefix again, causing infinite loops.
Related Problems¶
| # | Problem | Difficulty | Similarity |
|---|---|---|---|
| 1 | 189. Rotate Array | Same idea on arrays | |
| 2 | 25. Reverse Nodes in k-Group | Linked-list pointer rewrites | |
| 3 | 206. Reverse Linked List | Pointer manipulation |
Visual Animation¶
Interactive animation: animation.html
The animation includes: - Linked-list rendering with pointer arrows - Step-by-step length count, mod-k reduction, walk to new tail - "Cut" animation showing the two pointer rewrites - Adjustable list length (1..10) and k slider