A clear guide to rotating a linked list to the right by k places using a circular list.
Problem Restatement
We are given the head of a singly linked list and an integer k.
We need to rotate the list to the right by k places.
A right rotation means:
- Remove the last node.
- Move it to the front.
Repeat this process k times.
The official constraints are 0 <= number of nodes <= 500, -100 <= Node.val <= 100, and 0 <= k <= 2 * 10^9. (leetcode.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | Head of a singly linked list and integer k |
| Output | Head of the rotated linked list |
| Rotation | Move the last node to the front |
| Direction | Right rotation |
Linked list node definition:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = nextFunction shape:
def rotateRight(head: ListNode, k: int) -> ListNode:
...Examples
For:
1 -> 2 -> 3 -> 4 -> 5
k = 2After one rotation:
5 -> 1 -> 2 -> 3 -> 4After two rotations:
4 -> 5 -> 1 -> 2 -> 3So the answer is:
4 -> 5 -> 1 -> 2 -> 3For:
0 -> 1 -> 2
k = 4The list length is 3.
Rotating by 4 is the same as rotating by:
4 % 3 = 1So the answer is:
2 -> 0 -> 1First Thought: Rotate One Step at a Time
One direct approach is:
- Find the last node.
- Remove it.
- Move it to the front.
- Repeat
ktimes.
This works, but each rotation requires scanning the list to find the last node.
If the list has n nodes, each rotation costs O(n).
Doing that k times gives:
O(n * k)That is too slow when k is large.
Key Insight
A linked list rotation is really a cut-and-reconnect operation.
Suppose:
1 -> 2 -> 3 -> 4 -> 5Rotating right by 2 means:
4 -> 5 -> 1 -> 2 -> 3The new head becomes the (n - k)th node from the front.
Instead of rotating repeatedly:
- Compute the list length.
- Connect the tail back to the head, forming a cycle.
- Find the new tail.
- Break the cycle.
This solves the problem in one traversal.
Reduce k
Rotating by the list length changes nothing.
For example:
1 -> 2 -> 3Rotate by 3:
1 -> 2 -> 3Rotate by 6:
1 -> 2 -> 3So we only care about:
k % lengthAlgorithm
Handle empty lists first.
Then:
- Traverse the list once to compute the length and find the tail.
- Compute:
k %= length- If
k == 0, return the original list. - Connect the tail to the head to form a cycle.
- Move
length - k - 1steps from the head to find the new tail. - The node after the new tail becomes the new head.
- Break the cycle.
Correctness
Let the list length be n.
Rotating right by k moves the last k nodes to the front while preserving their order. After reducing with k %= n, we only need to consider 0 <= k < n.
The new head must therefore be the node originally at position n - k. The node immediately before it becomes the new tail.
The algorithm first connects the original tail to the original head, forming a cycle. This preserves the relative order of all nodes while allowing the list to wrap around naturally.
Then the algorithm walks n - k - 1 steps from the original head. This reaches the node immediately before the desired new head, so this node is the correct new tail.
The node after the new tail is assigned as the new head. Breaking the cycle after the new tail restores a valid singly linked list with exactly the required rotation.
Therefore the algorithm returns the correctly rotated list.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | The list is traversed a constant number of times |
| Space | O(1) | Only pointers and counters are used |
Implementation
class Solution:
def rotateRight(
self,
head: Optional[ListNode],
k: int,
) -> Optional[ListNode]:
if not head or not head.next or k == 0:
return head
length = 1
tail = head
while tail.next:
tail = tail.next
length += 1
k %= length
if k == 0:
return head
tail.next = head
steps = length - k - 1
new_tail = head
for _ in range(steps):
new_tail = new_tail.next
new_head = new_tail.next
new_tail.next = None
return new_headCode Explanation
First, handle trivial cases:
if not head or not head.next or k == 0:
return headThen compute the length and find the tail:
length = 1
tail = head
while tail.next:
tail = tail.next
length += 1Reduce unnecessary rotations:
k %= lengthIf the reduced rotation is zero, the list stays unchanged:
if k == 0:
return headNow connect the tail back to the head:
tail.next = headThe list is now circular.
We need the new tail.
It is located:
length - k - 1steps from the original head.
Move to that node:
new_tail = head
for _ in range(steps):
new_tail = new_tail.nextThe next node becomes the new head:
new_head = new_tail.nextBreak the cycle:
new_tail.next = NoneFinally:
return new_headTesting
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build(values):
dummy = ListNode()
cur = dummy
for v in values:
cur.next = ListNode(v)
cur = cur.next
return dummy.next
def to_list(head):
ans = []
while head:
ans.append(head.val)
head = head.next
return ans
def run_tests():
s = Solution()
head = build([1, 2, 3, 4, 5])
assert to_list(s.rotateRight(head, 2)) == [4, 5, 1, 2, 3]
head = build([0, 1, 2])
assert to_list(s.rotateRight(head, 4)) == [2, 0, 1]
head = build([1])
assert to_list(s.rotateRight(head, 99)) == [1]
head = build([])
assert to_list(s.rotateRight(head, 3)) == []
head = build([1, 2])
assert to_list(s.rotateRight(head, 0)) == [1, 2]
print("all tests passed")
run_tests()| Test | Why |
|---|---|
[1,2,3,4,5], k = 2 | Standard example |
[0,1,2], k = 4 | Rotation larger than list length |
| Single node | Rotation should not change the list |
| Empty list | Edge case |
k = 0 | No rotation |