🧠
Bottom-Up Iterative Merge Sort on Linked List
💡 This approach eliminates recursion by iteratively merging sublists of increasing size, achieving O(1) space complexity and avoiding stack overflow.
Intuition
Start by merging pairs of nodes (size=1), then merge pairs of sorted lists of size 2, then size 4, and so on until the entire list is sorted.
Algorithm
- Calculate the length of the linked list.
- Iteratively merge sublists of size 1, then 2, then 4, doubling each time until size >= length.
- For each pass, traverse the list and merge pairs of sublists of the current size.
- Return the fully sorted list after all passes.
💡 This approach is more complex to implement but uses constant space and is robust for large inputs.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def merge(l1, l2):
dummy = ListNode(0)
tail = dummy
while l1 and l2:
if l1.val < l2.val:
tail.next = l1
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
tail.next = l1 or l2
return dummy.next
def sortList(head):
if not head or not head.next:
return head
# Get length
length = 0
node = head
while node:
length += 1
node = node.next
dummy = ListNode(0, head)
size = 1
while size < length:
cur = dummy.next
tail = dummy
while cur:
left = cur
right = split(left, size)
cur = split(right, size)
merged = merge(left, right)
tail.next = merged
while tail.next:
tail = tail.next
size <<= 1
return dummy.next
def split(head, size):
for i in range(size - 1):
if head:
head = head.next
else:
break
if not head:
return None
second = head.next
head.next = None
return second
# Example usage:
# head = ListNode(4, ListNode(2, ListNode(1, ListNode(3))))
# sorted_head = sortList(head)
# while sorted_head:
# print(sorted_head.val, end=' ')
# sorted_head = sorted_head.next
Line Notes
length = 0Calculate total length to control merge passes
while size < length:Double sublist size each iteration
right = split(left, size)Split list into sublists of given size
tail.next = mergedAttach merged sorted sublist to result
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; this.next = null; }
}
public class Solution {
private ListNode merge(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
tail = tail.next;
}
tail.next = (l1 != null) ? l1 : l2;
return dummy.next;
}
private ListNode split(ListNode head, int size) {
for (int i = 1; head != null && i < size; i++) {
head = head.next;
}
if (head == null) return null;
ListNode second = head.next;
head.next = null;
return second;
}
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
int length = 0;
ListNode node = head;
while (node != null) {
length++;
node = node.next;
}
ListNode dummy = new ListNode(0);
dummy.next = head;
for (int size = 1; size < length; size <<= 1) {
ListNode cur = dummy.next;
ListNode tail = dummy;
while (cur != null) {
ListNode left = cur;
ListNode right = split(left, size);
cur = split(right, size);
tail.next = merge(left, right);
while (tail.next != null) tail = tail.next;
}
}
return dummy.next;
}
// Example main method
// public static void main(String[] args) {
// ListNode head = new ListNode(4);
// head.next = new ListNode(2);
// head.next.next = new ListNode(1);
// head.next.next.next = new ListNode(3);
// Solution sol = new Solution();
// ListNode sorted = sol.sortList(head);
// while (sorted != null) {
// System.out.print(sorted.val + " ");
// sorted = sorted.next;
// }
// }
}
Line Notes
int length = 0;Calculate length of linked list for iteration control
for (int size = 1; size < length; size <<= 1)Double sublist size each pass
ListNode right = split(left, size);Split list into sublists of given size
tail.next = merge(left, right);Attach merged sorted sublist to result
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
ListNode* merge(ListNode* l1, ListNode* l2) {
ListNode dummy(0);
ListNode* tail = &dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
tail->next = l1;
l1 = l1->next;
} else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
tail->next = l1 ? l1 : l2;
return dummy.next;
}
ListNode* split(ListNode* head, int size) {
for (int i = 1; head && i < size; i++) {
head = head->next;
}
if (!head) return nullptr;
ListNode* second = head->next;
head->next = nullptr;
return second;
}
ListNode* sortList(ListNode* head) {
if (!head || !head->next) return head;
int length = 0;
ListNode* node = head;
while (node) {
length++;
node = node->next;
}
ListNode dummy(0);
dummy.next = head;
for (int size = 1; size < length; size <<= 1) {
ListNode* cur = dummy.next;
ListNode* tail = &dummy;
while (cur) {
ListNode* left = cur;
ListNode* right = split(left, size);
cur = split(right, size);
tail->next = merge(left, right);
while (tail->next) tail = tail->next;
}
}
return dummy.next;
}
// Example usage:
// int main() {
// ListNode* head = new ListNode(4);
// head->next = new ListNode(2);
// head->next->next = new ListNode(1);
// head->next->next->next = new ListNode(3);
// ListNode* sorted = sortList(head);
// while (sorted) {
// cout << sorted->val << " ";
// sorted = sorted->next;
// }
// return 0;
// }
Line Notes
int length = 0;Calculate length of linked list for iteration control
for (int size = 1; size < length; size <<= 1)Double sublist size each pass
ListNode* right = split(left, size);Split list into sublists of given size
tail->next = merge(left, right);Attach merged sorted sublist to result
function ListNode(val, next = null) {
this.val = val;
this.next = next;
}
function merge(l1, l2) {
const dummy = new ListNode(0);
let tail = dummy;
while (l1 && l2) {
if (l1.val < l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
tail = tail.next;
}
tail.next = l1 || l2;
return dummy.next;
}
function split(head, size) {
for (let i = 1; head && i < size; i++) {
head = head.next;
}
if (!head) return null;
const second = head.next;
head.next = null;
return second;
}
function sortList(head) {
if (!head || !head.next) return head;
let length = 0;
let node = head;
while (node) {
length++;
node = node.next;
}
const dummy = new ListNode(0, head);
for (let size = 1; size < length; size <<= 1) {
let cur = dummy.next;
let tail = dummy;
while (cur) {
const left = cur;
const right = split(left, size);
cur = split(right, size);
tail.next = merge(left, right);
while (tail.next) tail = tail.next;
}
}
return dummy.next;
}
// Example usage:
// let head = new ListNode(4, new ListNode(2, new ListNode(1, new ListNode(3))));
// let sorted = sortList(head);
// while (sorted) {
// console.log(sorted.val);
// sorted = sorted.next;
// }
Line Notes
let length = 0;Calculate length of linked list for iteration control
for (let size = 1; size < length; size <<= 1)Double sublist size each pass
const right = split(left, size);Split list into sublists of given size
tail.next = merge(left, right);Attach merged sorted sublist to result
TimeO(n log n)
SpaceO(1) auxiliary space
Each pass merges pairs of sublists covering all nodes, doubling sublist size each pass, totaling O(log n) passes and O(n) work each.
💡 For n=20, about 86 operations with no recursion stack overhead, making it efficient for large inputs.
Interview Verdict: Accepted and optimal for in-place sorting without recursion
This approach is ideal for large inputs or environments where recursion depth is limited.