โ† Back to DevBytes

Solving Rotate List in Python: Step-by-Step Guide

Introduction to Rotate List

The Rotate List problem is a classic algorithmic challenge frequently encountered in coding interviews and competitive programming. Given the head of a singly linked list and an integer k, the task is to rotate the list to the right by k places. This means the last k nodes are moved to the front of the list. Understanding how to manipulate linked list pointers efficiently is a foundational skill that translates directly to real-world scenarios such as buffer rotation, circular queues, and memory management in low-level systems.

What Is the Rotate List Problem?

Formally, the problem is defined as follows: you are given the head of a singly linked list and a non-negative integer k. You must rotate the list to the right by k positions. If k is larger than the length of the list, the rotation wraps around because rotating a list of length n by n positions returns the original list.

For example, consider the linked list 1 -> 2 -> 3 -> 4 -> 5 and k = 2. After rotating right by 2, the result becomes 4 -> 5 -> 1 -> 2 -> 3. The last two nodes (4 and 5) are moved to the front, while the remaining nodes shift right.

Edge Cases to Consider

Why It Matters

Mastering the Rotate List problem sharpens your ability to reason about pointer manipulation, a critical skill when working with data structures like linked lists, trees, and graphs. In production systems, similar rotation logic appears in:

Additionally, this problem is a favorite among interviewers at major tech companies because it tests multiple concepts simultaneously: traversal, cycle detection, modular arithmetic, and careful pointer rewiring.

Step-by-Step Approach

Step 1: Handle Trivial Cases

If the list is empty, contains a single node, or k is zero, return the head immediately. These cases require no rotation and skipping them early prevents unnecessary computation.

Step 2: Compute the Length of the List

Traverse the list from the head to the tail, counting nodes along the way. Keep a reference to the tail node because you will need it later to close the list into a ring.

Step 3: Normalize k

Since rotating by the length of the list produces the same list, compute k = k % length. If the result is zero, no rotation is needed and you can return the head as-is.

Step 4: Find the New Tail

The new tail of the rotated list is located at position length - k - 1 from the head (using zero-based indexing). Traverse from the head until you reach this node. The node immediately after it becomes the new head.

Step 5: Rewire the Pointers

Break the link between the new tail and the new head. Then, connect the old tail to the old head, forming the rotated list. Return the new head.

Complete Python Implementation

Below is the full implementation. First, we define the ListNode class, then the rotateRight function that performs the rotation.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


def rotateRight(head, k):
    # Step 1: Handle trivial cases
    if not head or not head.next or k == 0:
        return head

    # Step 2: Compute the length and find the tail
    length = 1
    tail = head
    while tail.next:
        tail = tail.next
        length += 1

    # Step 3: Normalize k
    k = k % length
    if k == 0:
        return head

    # Step 4: Find the new tail (at position length - k - 1)
    new_tail = head
    for _ in range(length - k - 1):
        new_tail = new_tail.next

    # Step 5: Rewire the pointers
    new_head = new_tail.next
    new_tail.next = None
    tail.next = head

    return new_head

Helper Functions for Testing

To verify the implementation, we need utility functions to build a linked list from a Python list and to convert a linked list back into a Python list for easy inspection.

def build_list(values):
    if not values:
        return None
    head = ListNode(values[0])
    current = head
    for val in values[1:]:
        current.next = ListNode(val)
        current = current.next
    return head


def list_to_array(head):
    result = []
    while head:
        result.append(head.val)
        head = head.next
    return result


# Example usage
head = build_list([1, 2, 3, 4, 5])
rotated = rotateRight(head, 2)
print(list_to_array(rotated))  # Output: [4, 5, 1, 2, 3]

# Edge case: k larger than length
head2 = build_list([1, 2, 3])
rotated2 = rotateRight(head2, 5)
print(list_to_array(rotated2))  # Output: [2, 3, 1]

# Edge case: empty list
print(rotateRight(None, 3))  # Output: None

# Edge case: single node
head3 = build_list([42])
print(list_to_array(rotateRight(head3, 10)))  # Output: [42]

Complexity Analysis

The algorithm runs in O(n) time, where n is the number of nodes in the list. We traverse the list twice in the worst case: once to compute the length and locate the tail, and once to find the new tail. The space complexity is O(1) because we only use a constant number of pointers regardless of the input size.

This is optimal because you must inspect every node at least once to determine the length of the list, and you cannot rotate without traversing to the break point.

Best Practices

Common Pitfalls

One frequent mistake is forgetting to break the link between the new tail and the new head, which creates a cycle in the list. Always set new_tail.next = None after capturing the new head reference. Another common error is skipping the modulo operation, leading to excessive traversal when k is very large. Finally, be careful with off-by-one errors when locating the new tail: the loop should run length - k - 1 times, not length - k.

Alternative Approach: Closing the List into a Ring

An elegant variation connects the tail to the head first, forming a circular list, then breaks the ring at the correct position. This reduces the number of distinct pointer operations and can be easier to reason about.

def rotateRightRing(head, k):
    if not head or not head.next or k == 0:
        return head

    # Compute length and close the ring
    length = 1
    tail = head
    while tail.next:
        tail = tail.next
        length += 1
    tail.next = head  # Form a circular list

    # Normalize k
    k = k % length

    # Find the new tail, then break the ring
    new_tail = head
    for _ in range(length - k - 1):
        new_tail = new_tail.next

    new_head = new_tail.next
    new_tail.next = None

    return new_head

This approach produces the same result with identical complexity but structures the logic differently. Some developers find it more intuitive because the list is treated as a ring until the final break.

Conclusion

The Rotate List problem is a deceptively simple exercise that reinforces essential linked list manipulation skills. By carefully handling edge cases, normalizing k with modular arithmetic, and rewiring pointers with precision, you can solve the problem in linear time with constant space. Whether you choose the straightforward two-pass approach or the ring-closing variation, the key is to maintain clear references to the critical nodes and break links in the correct order. Mastering this pattern will strengthen your overall ability to work with pointer-based data structures and prepare you for more advanced challenges involving list reordering, cycle detection, and in-place transformations.

๐Ÿ›  Tools from DevBytes

Inventory Tracker Pro โ€” Excel inventory system, low-stock alerts ยท $19
AI Dev Kit for Mac โ€” local AI dev environment templates ยท $9.99
KeyMapper for Mac โ€” custom keyboard shortcut toolkit ยท $7.99

โ† Back to all articles