2 minute read

Problem Statement

problem-148

Intuition

When tackling this problem, my initial thoughts revolve around the concept of merge sort, which is efficient for sorting linked lists. The idea is to recursively divide the list into smaller sublists until each sublist contains only one element, and then merge these sublists while ensuring the elements are in ascending order.

Approach

To implement this approach, I’ll define a function to split the list into two halves, find the middle node using the slow and fast pointer technique. Then, I’ll recursively sort the two halves and merge them back together in sorted order.

Complexity

  • Time complexity: O(n logn )

  • Space complexity: O(n)

Code

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        def getMiddleNode(head_node):
            slow = fast = head_node
            prev = None
            while fast and fast.next:
                prev = slow
                slow = slow.next
                fast = fast.next.next
            return prev, slow

        def merge(l1, l2):
            dummy = ListNode(0)
            curr = dummy
            while l1 and l2:
                if l1.val < l2.val:
                    curr.next = l1
                    l1 = l1.next
                else:
                    curr.next = l2
                    l2 = l2.next
                curr = curr.next

            curr.next = l1 if l1 is not None else l2
            return dummy.next


        def helper(node):
            if not node:
                return
            if node and not node.next:
                return node
            l1_head = node
            prev, l2_head = getMiddleNode(node)
            if prev:
                prev.next = None
            left_node = helper(l1_head)
            right_node = helper(l2_head)
            return merge(left_node, right_node)


        return helper(head)

For following up questions - EDITORIAL SOLUTION

O(1) space complexity

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if head is None or head.next is None:
            return head
        n = self.getCount(head)
        start = head
        dummyHead = ListNode()
        size = 1
        while size < n:
            self.tail = dummyHead
            while start is not None:
                if start.next is None:
                    self.tail.next = start
                    break
                mid = self.split(start, size)
                self.merge(start, mid)
                start = self.nextSubList
            start = dummyHead.next
            size *= 2
        return dummyHead.next

    def split(self, start, size):
        midPrev = start
        end = start.next
        # Use fast and slow approach to find middle and end of second linked list
        for index in range(1, size):
            if end and end.next:
                end = end.next.next
            else:
                if end:
                    end = end.next
            if midPrev.next:
                midPrev = midPrev.next
        mid = midPrev.next
        midPrev.next = None
        self.nextSubList = end.next if end else None
        if end:
            end.next = None
        # Return the start of second linked list
        return mid

    def merge(self, list1, list2):
        dummyHead = ListNode()
        newTail = dummyHead
        while list1 and list2:
            if list1.val < list2.val:
                newTail.next = list1
                list1 = list1.next
            else:
                newTail.next = list2
                list2 = list2.next
            newTail = newTail.next
        newTail.next = list1 if list1 else list2
        # Traverse till the end of merged list to get the newTail
        while newTail.next:
            newTail = newTail.next
        # Link the old tail with the head of merged list
        self.tail.next = dummyHead.next
        # Update the old tail to the new tail of merged list
        self.tail = newTail

    def getCount(self, head):
        cnt = 0
        ptr = head
        while ptr:
            ptr = ptr.next
            cnt += 1
        return cnt