08. Merge Sort for Linked List
β GFG solution to the Merge Sort for Linked List problem: efficiently sort a linked list using divide and conquer approach with merge sort algorithm. π
The problem can be found at the following link: π Question Link
π§© Problem Description
You are given the head of a linked list. Your task is to sort the given linked list using Merge Sort algorithm.
Merge Sort is a divide-and-conquer algorithm that works by recursively dividing the list into smaller sublists, sorting them, and then merging them back together in sorted order.
π Examples
Example 1
Input:
Linked List: 40 -> 20 -> 60 -> 10 -> 50 -> 30
Output: 10 -> 20 -> 30 -> 40 -> 50 -> 60
Explanation: After sorting the given linked list, the resultant list will be in ascending order.Example 2
Input:
Linked List: 9 -> 5 -> 2 -> 8
Output: 2 -> 5 -> 8 -> 9
Explanation: After sorting the given linked list, the resultant list will be in ascending order.π Constraints
$1 \le \text{number of nodes} \le 10^5$
$0 \le \text{node->data} \le 10^6$
β
My Approach
The optimal approach uses the Divide and Conquer strategy with Merge Sort algorithm specifically adapted for linked lists:
Recursive Divide and Conquer
Base Case:
If the list is empty or has only one node, it's already sorted - return it.
Divide Phase:
Find the middle of the linked list using the slow-fast pointer technique.
Split the list into two halves at the middle point.
Break the connection between the two halves.
Conquer Phase:
Recursively sort the left half.
Recursively sort the right half.
Merge Phase:
Merge the two sorted halves into a single sorted list.
Compare elements from both lists and link them in ascending order.
Return Result:
Return the head of the merged sorted list.
Key Implementation Details:
Finding Middle: Use two pointers - slow moves one step, fast moves two steps. When fast reaches end, slow is at middle.
Merging: Compare data of nodes from both lists and link smaller ones first, ensuring sorted order.
Memory Efficient: No extra space needed for arrays - works directly with list nodes.
π Time and Auxiliary Space Complexity
Expected Time Complexity: O(n log n), where n is the number of nodes in the linked list. We divide the list into halves (log n levels) and at each level we traverse all n nodes for merging.
Expected Auxiliary Space Complexity: O(log n), due to the recursion stack depth. In the worst case, we have log n recursive calls on the call stack simultaneously.
π§βπ» Code (C++)
β‘ View Alternative Approaches with Code and Analysis
π 2οΈβ£ Iterative Bottom-Up Approach
π‘ Algorithm Steps:
Start with sublist size 1, then 2, 4, 8, etc.
Merge adjacent sublists of current size iteratively.
Double the sublist size in each iteration until it exceeds list length.
No recursion overhead, purely iterative solution.
π Complexity Analysis:
Time: β±οΈ O(n log n) - Same as recursive but no stack overhead
Auxiliary Space: πΎ O(1) - Constant space, no recursion stack
β
Why This Approach?
No recursion stack overflow risk for large lists
Constant space complexity
Better for memory-constrained environments
π 3οΈβ£ Array Conversion Approach
π‘ Algorithm Steps:
Convert linked list to array for random access.
Apply standard merge sort on array with O(log n) space.
Reconstruct linked list from sorted array.
Leverage array's cache locality for better performance.
π Complexity Analysis:
Time: β±οΈ O(n log n) - Standard sorting time complexity
Auxiliary Space: πΎ O(n) - Array storage for all elements
β
Why This Approach?
Simplest implementation using standard library
Better cache performance due to array access
Easy to understand and debug
π 4οΈβ£ Three-Way Split Approach
π‘ Algorithm Steps:
Split list into three parts instead of two for better balance.
Recursively sort each of the three sublists.
Merge three sorted sublists using a three-way merge.
Reduces recursion depth compared to binary splitting.
π Complexity Analysis:
Time: β±οΈ O(n logβ n) - Slightly better than binary split
Auxiliary Space: πΎ O(logβ n) - Reduced recursion depth
β
Why This Approach?
Reduced recursion depth for very large lists
More balanced splits in some cases
Interesting algorithmic variation
π π Comparison of Approaches
π Approach
β±οΈ Time Complexity
πΎ Space Complexity
β Pros
β οΈ Cons
π·οΈ Recursive Divide-Conquer
π’ O(n log n)
π‘ O(log n)
π Simple and elegant
π Stack space usage
π Iterative Bottom-Up
π’ O(n log n)
π’ O(1)
πΎ Constant space
π Complex implementation
π Array Conversion
π’ O(n log n)
π‘ O(n)
π― Simple using std::sort
πΎ Extra space for array
π Three-Way Split
π’ O(n logβ n)
π‘ O(logβ n)
β Reduced recursion depth
π§ More complex merge logic
π Best Choice Recommendation
π― Scenario
ποΈ Recommended Approach
π₯ Performance Rating
π General purpose/Interview
π₯ Recursive Divide-Conquer
β β β β β
π Memory constrained
π₯ Iterative Bottom-Up
β β β β β
π§ Quick implementation
π₯ Array Conversion
β β β ββ
π― Very large lists
π Iterative Bottom-Up
β β β β β
β Code (Java)
π Code (Python)
π§ Contribution and Support
For discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: π¬ Any Questions?. Let's make this learning journey more collaborative!
β If you find this helpful, please give this repository a star! β
πVisitor Count
π§βπ» Code (C++)
β‘ View Alternative Approaches with Code and Analysis
π 2οΈβ£ Iterative Bottom-Up Approach
π‘ Algorithm Steps:
Start with sublist size 1, then 2, 4, 8, etc.
Merge adjacent sublists of current size iteratively.
Double the sublist size in each iteration until it exceeds list length.
No recursion overhead, purely iterative solution.
π Complexity Analysis:
Time: β±οΈ O(n log n) - Same as recursive but no stack overhead
Auxiliary Space: πΎ O(1) - Constant space, no recursion stack
β
Why This Approach?
No recursion stack overflow risk for large lists
Constant space complexity
Better for memory-constrained environments
π 3οΈβ£ Array Conversion Approach
π‘ Algorithm Steps:
Convert linked list to array for random access.
Apply standard merge sort on array with O(log n) space.
Reconstruct linked list from sorted array.
Leverage array's cache locality for better performance.
π Complexity Analysis:
Time: β±οΈ O(n log n) - Standard sorting time complexity
Auxiliary Space: πΎ O(n) - Array storage for all elements
β
Why This Approach?
Simplest implementation using standard library
Better cache performance due to array access
Easy to understand and debug
π 4οΈβ£ Three-Way Split Approach
π‘ Algorithm Steps:
Split list into three parts instead of two for better balance.
Recursively sort each of the three sublists.
Merge three sorted sublists using a three-way merge.
Reduces recursion depth compared to binary splitting.
π Complexity Analysis:
Time: β±οΈ O(n logβ n) - Slightly better than binary split
Auxiliary Space: πΎ O(logβ n) - Reduced recursion depth
β
Why This Approach?
Reduced recursion depth for very large lists
More balanced splits in some cases
Interesting algorithmic variation
π π Comparison of Approaches
π Approach
β±οΈ Time Complexity
πΎ Space Complexity
β Pros
β οΈ Cons
π·οΈ Recursive Divide-Conquer
π’ O(n log n)
π‘ O(log n)
π Simple and elegant
π Stack space usage
π Iterative Bottom-Up
π’ O(n log n)
π’ O(1)
πΎ Constant space
π Complex implementation
π Array Conversion
π’ O(n log n)
π‘ O(n)
π― Simple using std::sort
πΎ Extra space for array
π Three-Way Split
π’ O(n logβ n)
π‘ O(logβ n)
β Reduced recursion depth
π§ More complex merge logic
π Best Choice Recommendation
π― Scenario
ποΈ Recommended Approach
π₯ Performance Rating
π General purpose/Interview
π₯ Recursive Divide-Conquer
β β β β β
π Memory constrained
π₯ Iterative Bottom-Up
β β β β β
π§ Quick implementation
π₯ Array Conversion
β β β ββ
π― Very large lists
π Iterative Bottom-Up
β β β β β
β Code (Java)
π Code (Python)
Last updated