Close Menu
    Code and Debug
    • Home
    • Our Courses
    • Blog
    • About Us
    • Contact Us
    Facebook X (Twitter) Instagram YouTube WhatsApp
    • Home
    • Our Courses
    • Blog
    • About Us
    • Contact Us
    Facebook Instagram YouTube LinkedIn WhatsApp
    Code and Debug – BlogCode and Debug – Blog
    Code and Debug – BlogCode and Debug – Blog
    Home»Data Structures & Algorithms»Sort List | Leetcode 148 | Optimal Merge Sort
    Data Structures & Algorithms

    Sort List | Leetcode 148 | Optimal Merge Sort

    codeanddebugBy codeanddebug15 July 2025No Comments7 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Featured image of a question to sort a singly linked list
    Share
    Facebook Twitter LinkedIn Pinterest Email

    Given the head of a linked list, return the list after sorting it in ascending order.

    Here’s the [Problem Link] to begin with.

    Example 1:

    Input: head = [4,2,1,3]
    Output: [1,2,3,4]

    Example 2:

    Input: head = [-1,5,3,4,0]
    Output: [-1,0,3,4,5]

    Example 3:

    Input: head = []
    Output: []

    Constraints:

    • The number of nodes in the list is in the range [0, 5 * 104].
    • -105 <= Node.val <= 105

    Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)?

    Contents:
     [show]
    • Solution 1: Brute Force Approach (Using Array)
      • Intuition
      • Detailed Approach
      • Code
      • Code Explanation
      • Dry Run
      • Time and Space Complexity
      • Simplifying It
    • Solution 2: Optimal Approach (Merge Sort)
      • Intuition
      • Detailed Approach
      • Code
      • Code Explanation
      • Dry Run
      • Time and Space Complexity
      • Simplifying It
    • Summary

    Solution 1: Brute Force Approach (Using Array)

    Intuition

    Think of this like organizing a messy bookshelf! One simple way is to take all the books down, put them in a pile, sort that pile, and then put the books back on the shelf in the correct order. We do the same thing here – extract all values from the linked list, sort them in an array, and then put them back in the same linked list structure.

    Detailed Approach

    1. Extract Values: Walk through the linked list and collect all node values in an array
    2. Sort Array: Use the built-in sort function to sort the array of values
    3. Put Back Values: Walk through the linked list again and replace each node’s value with the sorted values
    4. Return Result: The linked list now contains values in sorted order

    Code

    class Solution:
        def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
            values = []
            current_node = head
    
            # First pass: Collect all values from the linked list
            while current_node:
                values.append(current_node.val)  # Add current node's value to array
                current_node = current_node.next
    
            # Sort the values using built-in sort
            values.sort()
    
            # Second pass: Reassign the sorted values to the linked list nodes
            current_node = head
            index = 0
            while current_node:
                current_node.val = values[index]  # Replace with sorted value
                index += 1
                current_node = current_node.next
    
            return head
    Python

    Code Explanation

    We use a two-pass approach here. In the first pass, we walk through the entire linked list and copy all the values into a regular array. Then we sort this array using Python’s built-in sort function, which is very efficient. In the second pass, we walk through the linked list again and replace each node’s value with the corresponding sorted value from our array. The structure of the linked list remains the same – only the values change.

    Dry Run

    Let’s trace through: 4 -> 2 -> 1 -> 3

    First Pass (Extract Values):

    • Visit node 4: values =
    • Visit node 2: values =
    • Visit node 1: values = 
    • Visit node 3: values = 

    Sort Array:

    • values.sort() → values = 

    Second Pass (Put Back Values):

    • Node 4 becomes 1: 1 -> 2 -> 1 -> 3
    • Node 2 becomes 2: 1 -> 2 -> 1 -> 3
    • Node 1 becomes 3: 1 -> 2 -> 3 -> 3
    • Node 3 becomes 4: 1 -> 2 -> 3 -> 4

    Result: 1 -> 2 -> 3 -> 4

    Time and Space Complexity

    • Time Complexity: O(n log n) – The sorting step dominates the time complexity
    • Space Complexity: O(n) – We store all values in the array

    Simplifying It

    This approach is like taking notes from a messy notebook, organizing those notes separately, and then rewriting them back in the same notebook in order. It’s easy to understand and implement, but requires extra space to store all the values temporarily.


    Solution 2: Optimal Approach (Merge Sort)

    Intuition

    Imagine you have a huge pile of playing cards to sort. Instead of sorting the entire pile at once, you could split it into smaller piles, sort each small pile, and then merge the sorted piles back together. This is exactly what merge sort does! It’s like having a team of people where each person sorts a small pile, then they work together to combine all the sorted piles into one final sorted pile.

    Detailed Approach

    1. Base Case: If the list has 0 or 1 nodes, it’s already sorted
    2. Split: Find the middle of the list and split it into two halves
    3. Conquer: Recursively sort both halves
    4. Merge: Combine the two sorted halves into one sorted list
    5. Return: The merged result is our final sorted list

    Code

    class Solution:
        def getMiddle(self, head: ListNode) -> ListNode:
            slow = head
            fast = head
            # Use slow and fast pointers to find middle
            while fast.next and fast.next.next:
                slow = slow.next      # Move slow 1 step
                fast = fast.next.next # Move fast 2 steps
            return slow  # Slow pointer ends at the middle (or just before)
    
        def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
            dummy = ListNode()  # Dummy node to simplify merging
            tail = dummy
            
            # Merge nodes by comparing values
            while l1 and l2:
                if l1.val < l2.val:
                    tail.next = l1    # Take smaller value from l1
                    l1 = l1.next
                else:
                    tail.next = l2    # Take smaller value from l2
                    l2 = l2.next
                tail = tail.next
    
            # Append remaining elements from either list
            tail.next = l1 or l2
            return dummy.next  # Skip the initial dummy node
    
        def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
            if not head or not head.next:  # Base case: empty or single-node list
                return head
    
            # Step 1: Split the list into two halves
            mid = self.getMiddle(head)
            left = head
            right = mid.next
            mid.next = None  # Disconnect the two halves
    
            # Step 2: Recursively sort the two halves
            left = self.sortList(left)
            right = self.sortList(right)
    
            # Step 3: Merge the sorted halves
            return self.mergeTwoLists(left, right)
    Python

    Code Explanation

    This solution uses the divide-and-conquer approach with three helper functions. The getMiddle function uses the tortoise and hare technique to find the middle node. The mergeTwoLists function takes two sorted linked lists and merges them into one sorted list by comparing values. The main sortList function recursively splits the list in half, sorts each half, and then merges them back together. This process continues until we have single-node lists (which are already sorted), then builds up the final sorted list.

    Dry Run

    Let’s trace through: 4 -> 2 -> 1 -> 3

    Initial Call: sortList([4 -> 2 -> 1 -> 3])

    • Find middle: between 2 and 1
    • Split: left = [4 -> 2], right = [1 -> 3]
    • Recursively sort both halves

    Left Half: sortList([4 -> 2])

    • Find middle: between 4 and 2
    • Split: left = , right =
    • Base case: both are single nodes, return as is
    • Merge and → [2 -> 4]

    Right Half: sortList([1 -> 3])

    • Find middle: between 1 and 3
    • Split: left = , right =
    • Base case: both are single nodes, return as is
    • Merge  and → [1 -> 3]

    Final Merge: mergeTwoLists([2 -> 4], [1 -> 3])

    • Compare 2 and 1: take 1 → 
    • Compare 2 and 3: take 2 → [1 -> 2]
    • Compare 4 and 3: take 3 → [1 -> 2 -> 3]
    • Append remaining 4 → [1 -> 2 -> 3 -> 4]

    Result: 1 -> 2 -> 3 -> 4

    Time and Space Complexity

    • Time Complexity: O(n log n) – We divide the list log n times, and each merge takes O(n) time
    • Space Complexity: O(log n) – Due to the recursion call stack (depth of recursion is log n)

    Simplifying It

    This approach is like organizing a large group of people by repeatedly splitting them into smaller groups, having each small group organize themselves, and then merging the organized groups back together. It’s more complex to understand but very efficient and doesn’t require extra space for storing all values like the brute force approach.

    Summary

    ApproachTimeSpaceDifficultyBest For
    Brute Force (Array)O(n log n)O(n)EasyLearning the concept
    Optimal (Merge Sort)O(n log n)O(log n)HardProduction code

    The merge sort approach is generally preferred because it uses less space while maintaining the same time complexity. However, the brute force approach is more intuitive and easier to understand when you’re first learning about sorting algorithms.

    Both solutions achieve the same time complexity, but the merge sort solution is more elegant and space-efficient. It also demonstrates important concepts like divide-and-conquer, recursion, and the two-pointer technique. The key insight is that we can sort a linked list without converting it to an array by cleverly splitting, sorting, and merging the sublists!

    Join our Advance DSA COURSE

    For any changes to the article, kindly email at code@codeanddebug.in or contact us at +91-9712928220.

    Hard Singly Linked List
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleDelete the Middle Node of a Linked List | Leetcode 2095 | Modified Tortoise and Hare
    Next Article Sort a linked list of 0s, 1s and 2s | GFG Question Explained
    codeanddebug
    • Website

    Related Posts

    Data Structures & Algorithms

    Intersection of Two Linked Lists | Leetcode 160 | Brute, Better and Optimal Solutions

    15 July 2025
    Data Structures & Algorithms

    Sort a linked list of 0s, 1s and 2s | GFG Question Explained

    15 July 2025
    Data Structures & Algorithms

    Delete the Middle Node of a Linked List | Leetcode 2095 | Modified Tortoise and Hare

    15 July 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Categories
    • Data Structures & Algorithms (129)
      • Beginner (51)
      • Expert (36)
      • Intermediate (42)
    • Uncategorised (1)
    Recent Posts

    Intersection of Two Linked Lists | Leetcode 160 | Brute, Better and Optimal Solutions

    15 July 2025

    Sort a linked list of 0s, 1s and 2s | GFG Question Explained

    15 July 2025

    Sort List | Leetcode 148 | Optimal Merge Sort

    15 July 2025

    Delete the Middle Node of a Linked List | Leetcode 2095 | Modified Tortoise and Hare

    15 July 2025

    Remove Nth Node From End of List | Leetcode 19

    15 July 2025
    Facebook Instagram YouTube LinkedIn WhatsApp
    © 2025 Code and Debug. All rights reserved.

    Type above and press Enter to search. Press Esc to cancel.