61. Rotate List - Explanation
Description
You are given the head of a linked list, rotate the list to the right by k places.
Example 1:
Input: head = [1,2,3,4,5,6], k = 3
Output: [4,5,6,1,2,3]Explanation:
Rotate 1: [6,1,2,3,4,5]
Rotate 2: [5,6,1,2,3,4]
Rotate 3: [4,5,6,1,2,3]
Example 2:
Input: head = [0,1,2], k = 4
Output: [2,0,1]Explanation:
Rotate 1: [2,0,1]
Rotate 2: [1,2,0]
Rotate 3: [0,1,2]
Rotate 4: [2,0,1]
Constraints:
0 <= Length of the list <= 500.-100 <= Node.val <= 1000 <= k <= 2,000,000,000
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Linked List Fundamentals - Understanding node structure, traversal, and how to find the length of a list
- Pointer Manipulation - Reconnecting nodes by changing next pointers to restructure the list
- Modular Arithmetic - Using modulo to normalize rotation count when it exceeds list length
- Circular Linked List - The technique of connecting tail to head to form a cycle, then breaking it at the new position
1. Convert To Array
Intuition
The challenge with linked lists is that we cannot directly access elements by index. One straightforward approach is to convert the list to an array, perform the rotation using array indexing, and then write the values back to the list nodes. This trades memory for simplicity, allowing us to use familiar array rotation logic.
Algorithm
- If the list is empty, return
null. - Traverse the list and store all node values in an array.
- Compute
k = k % nto handle rotations larger than the list length. - Traverse the list again, assigning values from the rotated positions:
- First, assign the last
kvalues from the array. - Then, assign the remaining values.
- First, assign the last
- Return the
headof the modified list.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head:
return None
arr, cur = [], head
while cur:
arr.append(cur.val)
cur = cur.next
n = len(arr)
k %= n
cur = head
for i in range(n - k, n):
cur.val = arr[i]
cur = cur.next
for i in range(n - k):
cur.val = arr[i]
cur = cur.next
return headTime & Space Complexity
- Time complexity:
- Space complexity:
2. Iteration
Intuition
Rotating a linked list by k means moving the last k nodes to the front. We can do this by finding the new tail (the node at position n - k - 1), breaking the list there, and reconnecting the old tail to the old head. The key insight is that we only need to find two positions: where to break the list and where to reconnect.
Algorithm
- If the list is empty, return
null. - Traverse the list to find its
lengthand thetailnode. - Compute
k = k % length. Ifk == 0, no rotation is needed. - Traverse to the node at position
length - k - 1(this will be the newtail). - Set the new
headto be the next node after the newtail. - Break the link by setting the new
tail'snexttonull. - Connect the old
tailto the oldhead. - Return the new
head.
# Definition for singly-linked list.
# 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:
return head
length, tail = 1, head
while tail.next:
tail = tail.next
length += 1
k = k % length
if k == 0:
return head
cur = head
for i in range(length - k - 1):
cur = cur.next
newHead = cur.next
cur.next = None
tail.next = head
return newHeadTime & Space Complexity
- Time complexity:
- Space complexity: extra space.
3. Iteration (Using One Pointer)
Intuition
We can simplify the two-pointer approach by first creating a circular list. Connect the tail to the head, then traverse n - k steps from the tail to find the new tail. Break the circle at that point. This approach uses a single pointer and avoids the need to track both the tail and find the break point separately.
Algorithm
- If the list is empty, return
null. - Traverse to find the
tailand count the lengthn. - Connect the
tailto thehead, forming a circular list. - Compute
k = k % nto handle large values ofk. - Move
n - ksteps from the current position (thetail) to reach the newtail. - The new
headis the node after the newtail. - Break the circle by setting the new
tail'snexttonull. - Return the new
head.
# Definition for singly-linked list.
# 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:
return head
cur, n = head, 1
while cur.next:
n += 1
cur = cur.next
cur.next = head
k %= n
for i in range(n - k):
cur = cur.next
head = cur.next
cur.next = None
return headTime & Space Complexity
- Time complexity:
- Space complexity: extra space.
Common Pitfalls
Not Handling Empty List or Single Node
Forgetting to check if the list is empty (head == null) or has only one node causes null pointer exceptions. A single-node list rotated by any amount remains unchanged, but the code must handle this edge case explicitly.
Forgetting to Normalize k by List Length
When k is larger than the list length or equals it, the rotation results in the same list. Failing to compute k = k % length before rotation leads to unnecessary traversals or incorrect results. Additionally, when k % length == 0, no rotation is needed and the original head should be returned.
Incorrectly Breaking and Reconnecting the List
When creating the new list structure, forgetting to set the new tail's next to null creates a cycle in the linked list. Similarly, forgetting to connect the old tail to the old head leaves the list disconnected. Both pointer updates are essential for correct rotation.
Sign in to join the discussion