Two Pointers (Slow/Fast) - Finding the middle of a linked list using the tortoise and hare technique
Merging Two Sorted Lists - Combining two sorted sequences into one sorted sequence
1. Convert To Array
Intuition
Linked lists are notoriously difficult to sort in place due to lack of random access. A straightforward workaround is to extract all node values into an array, sort the array using a built-in sorting algorithm, and then write the sorted values back into the linked list nodes. This leverages efficient array sorting while preserving the original list structure.
Algorithm
Traverse the linked list and collect all node values into an array.
Sort the array using a standard sorting algorithm.
Traverse the linked list again, updating each node's value with the corresponding sorted value from the array.
# Definition for singly-linked list.# class ListNode:# def __init__(self, val=0, next=None):# self.val = val# self.next = nextclassSolution:defsortList(self, head: Optional[ListNode])-> Optional[ListNode]:
arr =[]
cur = head
while cur:
arr.append(cur.val)
cur = cur.next
arr.sort()
cur = head
for val in arr:
cur.val = val
cur = cur.nextreturn head
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{publicListNodesortList(ListNode head){if(head ==null)returnnull;List<Integer> arr =newArrayList<>();ListNode cur = head;while(cur !=null){
arr.add(cur.val);
cur = cur.next;}Collections.sort(arr);
cur = head;for(int val : arr){
cur.val = val;
cur = cur.next;}return head;}}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/classSolution{public:
ListNode*sortList(ListNode* head){if(!head)returnnullptr;
vector<int> arr;
ListNode* cur = head;while(cur){
arr.push_back(cur->val);
cur = cur->next;}sort(arr.begin(), arr.end());
cur = head;for(int val : arr){
cur->val = val;
cur = cur->next;}return head;}};
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {ListNode}
*/sortList(head){if(!head)returnnull;let arr =[];let cur = head;while(cur){
arr.push(cur.val);
cur = cur.next;}
arr.sort((a, b)=> a - b);
cur = head;for(let val of arr){
cur.val = val;
cur = cur.next;}return head;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcsortList(head *ListNode)*ListNode {if head ==nil{returnnil}
arr :=[]int{}
cur := head
for cur !=nil{
arr =append(arr, cur.Val)
cur = cur.Next
}
sort.Ints(arr)
cur = head
for_, val :=range arr {
cur.Val = val
cur = cur.Next
}return head
}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funsortList(head: ListNode?): ListNode?{if(head ==null)returnnullval arr = mutableListOf<Int>()var cur = head
while(cur !=null){
arr.add(cur.`val`)
cur = cur.next
}
arr.sort()
cur = head
for(value in arr){
cur!!.`val` = value
cur = cur.next
}return head
}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcsortList(_ head:ListNode?)->ListNode?{guard head !=nilelse{returnnil}var arr =[Int]()var cur = head
while cur !=nil{
arr.append(cur!.val)
cur = cur?.next
}
arr.sort()
cur = head
for val in arr {
cur?.val = val
cur = cur?.next
}return head
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
2. Recursive Merge Sort
Intuition
Merge sort is well suited for linked lists because merging two sorted lists can be done efficiently without extra space by rearranging pointers. We recursively split the list in half using the slow and fast pointer technique to find the middle, sort each half, and then merge the two sorted halves together. This divide-and-conquer approach achieves O(n log n) time complexity.
Algorithm
Base case: if the list is empty or has one node, return it as is.
Use slow and fast pointers to find the middle of the list. The slow pointer will end at the node before the midpoint.
Split the list into two halves by setting the next pointer of the middle node to null.
Recursively sort the left half and right half.
Merge the two sorted halves by comparing node values and linking nodes in sorted order.
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/publicclassSolution{publicListNodeSortList(ListNode head){if(head ==null|| head.next ==null){return head;}ListNode left = head;ListNode right =GetMid(head);ListNode temp = right.next;
right.next =null;
right = temp;
left =SortList(left);
right =SortList(right);returnMerge(left, right);}privateListNodeGetMid(ListNode head){ListNode slow = head, fast = head.next;while(fast !=null&& fast.next !=null){
slow = slow.next;
fast = fast.next.next;}return slow;}privateListNodeMerge(ListNode list1,ListNode list2){ListNode dummy =newListNode(0);ListNode tail = dummy;while(list1 !=null&& list2 !=null){if(list1.val < list2.val){
tail.next = list1;
list1 = list1.next;}else{
tail.next = list2;
list2 = list2.next;}
tail = tail.next;}if(list1 !=null){
tail.next = list1;}if(list2 !=null){
tail.next = list2;}return dummy.next;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcsortList(head *ListNode)*ListNode {if head ==nil|| head.Next ==nil{return head
}
left := head
right :=getMid(head)
temp := right.Next
right.Next =nil
right = temp
left =sortList(left)
right =sortList(right)returnmerge(left, right)}funcgetMid(head *ListNode)*ListNode {
slow, fast := head, head.Next
for fast !=nil&& fast.Next !=nil{
slow = slow.Next
fast = fast.Next.Next
}return slow
}funcmerge(list1, list2 *ListNode)*ListNode {
dummy :=&ListNode{}
tail := dummy
for list1 !=nil&& list2 !=nil{if list1.Val < list2.Val {
tail.Next = list1
list1 = list1.Next
}else{
tail.Next = list2
list2 = list2.Next
}
tail = tail.Next
}if list1 !=nil{
tail.Next = list1
}if list2 !=nil{
tail.Next = list2
}return dummy.Next
}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funsortList(head: ListNode?): ListNode?{if(head?.next ==null){return head
}var left = head
var right =getMid(head)val temp = right?.next
right?.next =null
right = temp
left =sortList(left)
right =sortList(right)returnmerge(left, right)}privatefungetMid(head: ListNode?): ListNode?{var slow = head
var fast = head?.next
while(fast?.next !=null){
slow = slow?.next
fast = fast.next?.next
}return slow
}privatefunmerge(list1: ListNode?, list2: ListNode?): ListNode?{val dummy =ListNode(0)var tail: ListNode?= dummy
var l1 = list1
var l2 = list2
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
}if(l1 !=null){
tail?.next = l1
}if(l2 !=null){
tail?.next = l2
}return dummy.next
}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcsortList(_ head:ListNode?)->ListNode?{guard head !=nil&& head?.next !=nilelse{return head
}varleft= head
varright=getMid(head)let temp =right?.next
right?.next =nilright= temp
left=sortList(left)right=sortList(right)returnmerge(left,right)}privatefuncgetMid(_ head:ListNode?)->ListNode?{var slow = head
var fast = head?.next
while fast !=nil&& fast?.next !=nil{
slow = slow?.next
fast = fast?.next?.next
}return slow
}privatefuncmerge(_ list1:ListNode?,_ list2:ListNode?)->ListNode?{let dummy =ListNode(0)var tail:ListNode?= dummy
var l1 = list1
var l2 = list2
while l1 !=nil&& l2 !=nil{if l1!.val < l2!.val {
tail?.next = l1
l1 = l1?.next
}else{
tail?.next = l2
l2 = l2?.next
}
tail = tail?.next
}if l1 !=nil{
tail?.next = l1
}if l2 !=nil{
tail?.next = l2
}return dummy.next
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(logn) for recursion stack.
3. Iterative Merge Sort
Intuition
The recursive merge sort uses O(log n) space for the call stack. To achieve true O(1) extra space, we can implement merge sort iteratively using a bottom-up approach. Instead of recursively splitting the list, we start by treating each node as a sorted sublist of size 1, then merge adjacent pairs into sorted sublists of size 2, then 4, and so on until the entire list is sorted.
Algorithm
Count the total length of the linked list.
Use a dummy node to simplify head management during merges.
Start with a step size of 1 and double it each iteration until it reaches the list length.
For each step size, traverse the list and split off pairs of sublists of that size.
Merge each pair of sublists and attach the merged result to the previous portion of the list.
After all iterations complete, return the sorted list starting from dummy.next.
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcsortList(head *ListNode)*ListNode {if head ==nil|| head.Next ==nil{return head
}
length :=0
cur := head
for cur !=nil{
length++
cur = cur.Next
}
dummy :=&ListNode{Val:0, Next: head}
step :=1for step < length {
prev, curr := dummy, dummy.Next
for curr !=nil{
left := curr
right :=split(left, step)
curr =split(right, step)
merged :=merge(left, right)
prev.Next = merged
for prev.Next !=nil{
prev = prev.Next
}}
step *=2}return dummy.Next
}funcsplit(head *ListNode, step int)*ListNode {if head ==nil{returnnil}for i :=0; i < step-1&& head.Next !=nil; i++{
head = head.Next
}
nextPart := head.Next
head.Next =nilreturn nextPart
}funcmerge(list1, list2 *ListNode)*ListNode {
dummy :=&ListNode{}
tail := dummy
for list1 !=nil&& list2 !=nil{if list1.Val < list2.Val {
tail.Next = list1
list1 = list1.Next
}else{
tail.Next = list2
list2 = list2.Next
}
tail = tail.Next
}if list1 !=nil{
tail.Next = list1
}else{
tail.Next = list2
}return dummy.Next
}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funsortList(head: ListNode?): ListNode?{if(head?.next ==null){return head
}var length =0var cur = head
while(cur !=null){
length++
cur = cur.next
}val dummy =ListNode(0)
dummy.next = head
var step =1while(step < length){var prev: ListNode?= dummy
var curr = dummy.next
while(curr !=null){val left = curr
val right =split(left, step)
curr =split(right, step)val merged =merge(left, right)
prev?.next = merged
while(prev?.next !=null){
prev = prev.next
}}
step *=2}return dummy.next
}privatefunsplit(head: ListNode?, step: Int): ListNode?{var h = head
if(h ==null)returnnullfor(i in0 until step -1){if(h?.next ==null)break
h = h.next
}val nextPart = h?.next
h?.next =nullreturn nextPart
}privatefunmerge(list1: ListNode?, list2: ListNode?): ListNode?{val dummy =ListNode(0)var tail: ListNode?= dummy
var l1 = list1
var l2 = list2
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 ?: l2
return dummy.next
}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcsortList(_ head:ListNode?)->ListNode?{guard head !=nil&& head?.next !=nilelse{return head
}var length =0var cur = head
while cur !=nil{
length +=1
cur = cur?.next
}let dummy =ListNode(0)
dummy.next = head
var step =1while step < length {var prev:ListNode?= dummy
var curr = dummy.next
while curr !=nil{letleft= curr
letright=split(left, step)
curr =split(right, step)let merged =merge(left,right)
prev?.next = merged
while prev?.next !=nil{
prev = prev?.next
}}
step *=2}return dummy.next
}privatefuncsplit(_ head:ListNode?,_ step:Int)->ListNode?{guardvar h = head else{returnnil}for_in0..<(step -1){guard h.next !=nilelse{break}
h = h.next!}let nextPart = h.next
h.next =nilreturn nextPart
}privatefuncmerge(_ list1:ListNode?,_ list2:ListNode?)->ListNode?{let dummy =ListNode(0)var tail:ListNode?= dummy
var l1 = list1
var l2 = list2
while l1 !=nil&& l2 !=nil{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
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(1)
Common Pitfalls
Incorrect Mid-Point Calculation for List Splitting
When finding the middle of the linked list, starting fast at head instead of head.next can lead to incorrect splitting, especially for lists with two elements. This causes infinite recursion as the list never gets properly divided.
Forgetting to Disconnect the Two Halves
After finding the middle node, you must set mid.next = null to properly split the list into two separate halves. Failing to disconnect them results in the merge sort operating on overlapping lists, causing incorrect results or infinite loops.
Not Handling Edge Cases for Empty or Single-Node Lists
The base case must return immediately if head is null or head.next is null. Missing this check leads to null pointer exceptions or infinite recursion when the algorithm tries to split a list that cannot be divided further.