Merge sort a linked list

LeetCode problem 148. Sort List.

This problem is based on the simpler problem: Merge two sorted linked lists.

## Problem description

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

Follow up
Can you sort the linked list in O(*n* log *n*) time and O(1) memory (i.e. constant space)?
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`

## Approach 1: top-down (O(n log n) time complexity, O(log n) space complexity)

The **top-down** approach is as follows:

1. Find the midpoint of the linked list.
	* If there are even number of nodes, then find the *first* of the middle element.
2. Break the linked list after the midpoint.
	* Use two pointers `head1` and `head2` to store the heads of the two halves.
3. Recursively merge sort the two halves.
4. Merge the two sorted halves recursively.

### Complexity analysis

#### Time complexity

1. **The splitting phase**
	* Finding the middle point takes O(m) time for *m* nodes.
	* The total time for splitting *n* nodes is

	T
	(
	n

	)

	split

	=
	T
	(

	n
	2

	)

	split

	2
	⋅
	T
	(

	n
	4

	)

	split

	4
	⋅
	T
	(

	n
	8

	)

	split

	⋯

	n
	2

	⋅
	T
	(
	1

	)

	split

	{\displaystyle T(n)\_{\text{split}} = T(\frac{n}{2})\_{\text{split}} + 2 \cdot T(\frac{n}{4})\_{\text{split}} + 4 \cdot T(\frac{n}{8})\_{\text{split}} + \dots + \frac{n}{2} \cdot T(1)\_{\text{split}}}

	=
	O
	(

	n
	2

	⋅

	log

	2

	⁡

	n
	2

	)

	{\displaystyle = O(\frac{n}{2} \cdot \log\_{2}{\frac{n}{2}})}

	.
2. **The merging phase**
	* Merging *k* modes takes O(k) time.
	* The total time for merging *n* nodes is

	T
	(
	n

	)

	merge

	=
	2
	⋅
	T
	(

	n
	2

	)

	merge

	4
	⋅
	T
	(

	n
	4

	)

	merge

	⋯
	n
	⋅
	T
	(
	1

	)

	merge

	{\displaystyle T(n)\_{\text{merge}} = 2 \cdot T(\frac{n}{2})\_{\text{merge}} + 4 \cdot T(\frac{n}{4})\_{\text{merge}} + \dots + n \cdot T(1)\_{\text{merge}}}

	=
	O
	(
	n

	log

	2

	⁡

	n

	)

	{\displaystyle = O(n \log\_{2}{n})}

	.

Summing them up gives the total time complexity of **O(*n* log *n*)**.

#### Space complexity

Stack
During each recursive call we need to maintain the two pointers `head1` and `head2`. Since the recursion is executed depth-first, this results in the space complexity of O(log *n*).
Heap
We did not allocate any new objects. So the space complexity on heap is O(1).
The total time complexity is **O(log *n*)**.

### Solution

#### C++

```
/\*\*
 \* 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) {}
 \* };
 \*/
class Solution {
private:
 static ListNode\* findMid(ListNode\* head) {
 // find the midpoint of the list
 // if the total number of nodes is even, return the first of mid
 ListNode \*slow = head, \*fast = head;
 while (fast != nullptr) {
 fast = fast->next;
 if (fast != nullptr) {
 fast = fast->next;
 }
 if (fast != nullptr) {
 slow = slow->next;
 }
 }
 return slow;
 }

 static ListNode\* mergeSort(ListNode\* head) {
 // trivial cases
 // also the base case of recursion
 if ((head == nullptr) || (head->next == nullptr)) {
 return head;
 }

 // step 1: find midpoint, and break the list after midpoint
 ListNode \*mid = findMid(head);
 ListNode \*secondHead = mid->next;
 mid->next = nullptr;

 // step 2: merge sort the two halves
 head = mergeSort(head);
 secondHead = mergeSort(secondHead);

 // step 3: merge the two sorted halves
 head = mergeTwoSortedLists(head, secondHead);

 return head;
 }

 static ListNode\* mergeTwoSortedLists(ListNode\* head1, ListNode\* head2) {
 if (head1 == nullptr) {
 return head2;
 }
 else if (head2 == nullptr) {
 return head1;
 }

 ListNode dummyHead(0), \*pMerge = &dummyHead;
 ListNode \*p1 = head1, \*p2 = head2;
 while ((p1 != nullptr) || (p2 != nullptr)) {
 // if p1 has reached end (is nullptr) then use p2
 if (p1 == nullptr) {
 pMerge->next = p2;
 pMerge = pMerge->next;
 p2 = p2->next;
 }
 // if p2 has reached end (is nullptr) then use p1
 else if (p2 == nullptr) {
 pMerge->next = p1;
 pMerge = pMerge->next;
 p1 = p1->next;
 }
 // otherwise, use the node with the smaller value
 else {
 if (p1->val < p2->val) {
 pMerge->next = p1;
 pMerge = pMerge->next;
 p1 = p1->next;
 }
 else {
 pMerge->next = p2;
 pMerge = pMerge->next;
 p2 = p2->next;
 }
 }
 }

 return dummyHead.next;
 }

public:
 ListNode\* sortList(ListNode\* head) {
 // top-down recursive approach
 return mergeSort(head);
 }
};

```

#### Java

```
/\*\*
 \* 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; }
 \* }
 \*/
class Solution {
 private static ListNode findMid(ListNode head) {
 // find the midpoint of the list
 // if the total number of nodes is even, return the first of mid
 ListNode slow = head, fast = head;
 while (fast != null) {
 fast = fast.next;
 if (fast != null) {
 fast = fast.next;
 }
 if (fast != null) {
 slow = slow.next;
 }
 }
 return slow;
 }

 private static ListNode mergeSort(ListNode head) {
 // trivial cases
 // also the base case of recursion
 if ((head == null) || (head.next == null)) {
 return head;
 }

 // step 1: find midpoint, and break the list after midpoint
 ListNode mid = findMid(head);
 ListNode secondHead = mid.next;
 mid.next = null;

 // step 2: merge sort the two halves
 head = mergeSort(head);
 secondHead = mergeSort(secondHead);

 // step 3: merge the two sorted halves
 head = mergeTwoLists(head, secondHead);

 return head;
 }

 private static ListNode mergeTwoLists(ListNode head1, ListNode head2) {
 if (head1 == null) {
 return head2;
 }
 else if (head2 == null) {
 return head1;
 }

 ListNode dummyHead = new ListNode(0);
 ListNode pMerge = dummyHead;
 ListNode p1 = head1, p2 = head2;
 while ((p1 != null) || (p2 != null)) {
 // if p1 has reached end (is null) then use p2
 if (p1 == null) {
 pMerge.next = p2;
 pMerge = pMerge.next;
 p2 = p2.next;
 }
 // if p2 has reached end (is null) then use p1
 else if (p2 == null) {
 pMerge.next = p1;
 pMerge = pMerge.next;
 p1 = p1.next;
 }
 // otherwise, use the node with the smaller value
 else {
 if (p1.val < p2.val) {
 pMerge.next = p1;
 pMerge = pMerge.next;
 p1 = p1.next;
 }
 else {
 pMerge.next = p2;
 pMerge = pMerge.next;
 p2 = p2.next;
 }
 }
 }

 return dummyHead.next;
 }

 public ListNode sortList(ListNode head) {
 // top-down recursive approach
 return mergeSort(head);
 }
}

```

## Approach 2: bottom-up (O(*n* log *n*) time complexity, O(1) space complexity)

Noting that the top-bottom approach takes O(log *n*) space complexity due to the recursion calls, we need to think about if we can use an iterative approach to merge the sorted lists of sizes 1, 2, 4, 8, … so that we can achieve the follow-up requirement of O(1) space complexity.

Our **bottom-up approach** is as follows:

1. Interpret the linked list as a collection of size-1 linked lists, with each small linked list considered sorted. (Linked lists of size 0 or 1 are automatically considered sorted)
2. Iteratively split and merge small sorted linked lists by groups of 2 (= 1 + 1), 4 (= 2 + 2), 8 (= 4 + 4), …, from left to right, and reorder the nodes in the whole linked list *in-place*.
3. Repeat the iterations above until the whole linked list is merge-sorted completely.

### Split and merge

For small sizes of *k* = 1, 2, 4, 8, … (all are powers of 2), what we need to do are:

1. Dummy head
	1. Create a dummy head pointer.
	2. Set the initial **merged tail** to the dummy head.
2. Create a **`start` pointer** to scan the linked list from left to right. Initialize it to the head of the whole linked list.
3. **The splitting phase**
	1. From the position of the `start` pointer, find the midpoint of the segment from the head of the list with size *k*⋅2. The midpoint becomes the tail of the **first half** (with size *k*) of this segment.
	2. Now the head of the **second half** of this segment is right after the midpoint.
	3. Break the linked list after the midpoint (set its `next` pointer to `null`).
	4. Find the tail of the second half. Then the node right after the tail is the head of the **next sub-list**.
	5. Again, break the linked list after the tail (set its `next` pointer to `null`), so that the second half also has size *k*.
4. **The merging phase**
	1. Merge the first *k*⋅2 elements from the two lists after the split. Record the head and tail of this merged list.
	2. Set the previous merged tail's `next` pointer to the head of this merged list.
	3. Set the merged tail to the tail of this merged list.
5. Advance the `start` pointer to the head of the next sub-list.
6. Repeat the split-and-merge process from left to right of the linked list, until all the elements are processed in groups of *k*, i.e. the `start` pointer becomes `null`.

After every left-to-right processing of the linked list, we multiply *k* by 2 and repeat the split-and-merge process, until *k* ≥ the size of the whole linked list.

#### Helper pointers to save

 Splitting phase
2. `secondListHead`: Head of the second sub-list.
 Merging phase
2. `mergedTail`: Tail of the merged list. (Update the `mergedTail` at high level to the tail of this merged list after each merge. This tail is to be joined with the next `mergedHead`.)

#### Helper classes

 `MergeHelper` | 1. `mergedHead`: head of the merged two linked lists
2. `mergedTail`: tail of the merged two linked lists

### Complexity analysis

#### Time complexity

1. For *n* total nodes, we use *k* = 1, 2, 4, 8, …, until *k* ≥ *n*. So the number of possible *k*'s are

log

2

⁡

n

{\displaystyle \log\_{2}{n}}

.
2. For each *k* that is used, each split-and-merge process takes O(n) time, since each node is processed twice:
	1. Once for finding the breakpoints and splitting the linked list into smaller sub-lists of size *k*;
	2. Once for merging the small sub-lists of size *k* into sub-lists of size *k*⋅2.

So the total time complexity is **O(*n* log *n*)**.

#### Space complexity

Stack
For each iteration we only used the same, fixed set of helper pointers. So the space complexity on stack is O(1).
Heap
We only have allocated one instance of the `SplitHelper` class and `MergeHelper` class. Also the dummy nodes take a fixed amount of space. So the heap space complexity is O(1).
The total space complexity is **O(1)**.

### Solution

#### C++

```
/\*\*
 \* 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) {}
 \* };
 \*/
class Solution {
private:

 class SplitHelper {
 public:
 ListNode \*secondListHead = nullptr;
 ListNode \*nextSubListHead = nullptr;
 };

 class MergeHelper {
 public:
 ListNode \*mergedHead = nullptr;
 ListNode \*mergedTail = nullptr;
 };

 static int countTotalNodes(ListNode\* head) {
 int result = 0;
 for (ListNode \*curr = head; curr != nullptr; curr = curr->next) {
 ++result;
 }
 return result;
 }

 // split a linked list by a given splitSize
 // save the first and second segments (for merging)
 // and the head of the remaining sublist (for future iterations)
 // return the splitHelper
 static SplitHelper& split(ListNode\* head, const int& splitSize,
 SplitHelper& splitHelper) {
 // trivial case
 if ((head == nullptr) || (head->next == nullptr)) {
 splitHelper.secondListHead = nullptr;
 splitHelper.nextSubListHead = nullptr;
 return splitHelper;
 }

 // find the mid and tail
 // example: if splitSize == 4
 // then mid is index 3 from head, tail is index 7 from head
 ListNode \*slow = head, \*fast = head->next;
 for (int i = 0; i < splitSize - 1; ++i) {
 slow = (slow != nullptr) ? slow->next : nullptr;
 for (int j = 0; j < 2; ++j) {
 fast = (fast != nullptr) ? fast->next : nullptr;
 }
 }

 // break the linked list at specified points
 // also save the key nodes into splitHelper
 ListNode \*mid = slow, \*tail = fast;
 splitHelper.secondListHead = (mid != nullptr) ? mid->next : nullptr;
 if (mid != nullptr) {
 mid->next = nullptr;
 }
 splitHelper.nextSubListHead = (tail != nullptr) ? tail->next : nullptr;
 if (tail != nullptr) {
 tail->next = nullptr;
 }

 return splitHelper;
 }

 // merge two sorted linked lists
 // save the merged head (for joining with the previous merged list)
 // save the merged tail (for joining with the next merged list)
 // return the mergeHelper
 static MergeHelper& mergeTwoSortedLists(ListNode\* head1, ListNode\* head2,
 MergeHelper& mergeHelper) {

 ListNode dummyHead(0), \*mergedTail = &dummyHead;
 ListNode \*p1 = head1, \*p2 = head2;

 while ((p1 != nullptr) || (p2 != nullptr)) {
 if (p1 == nullptr) {
 // if p1 is nullptr then use p2
 mergedTail->next = p2;
 mergedTail = p2;
 p2 = p2->next;
 }
 else if (p2 == nullptr) {
 // if p2 is nullptr then use p1
 mergedTail->next = p1;
 mergedTail = p1;
 p1 = p1->next;
 }
 else {
 // use the smaller one
 if (p1->val < p2->val) {
 mergedTail->next = p1;
 mergedTail = p1;
 p1 = p1->next;
 }
 else {
 mergedTail->next = p2;
 mergedTail = p2;
 p2 = p2->next;
 }
 }
 }

 mergeHelper.mergedHead = dummyHead.next;
 mergeHelper.mergedTail = mergedTail;
 return mergeHelper;
 }

public:
 ListNode\* sortList(ListNode\* head) {
 // bottom-up approach

 // trivial cases
 if ((head == nullptr) || (head->next == nullptr)) {
 return head;
 }

 const int totalNodes = countTotalNodes(head);

 SplitHelper splitHelper;
 MergeHelper mergeHelper;

 ListNode dummyHead(0);
 dummyHead.next = head;

 // split and merge with sizes 1, 2, 4, 8, ...
 for (int splitSize = 1; splitSize < totalNodes; splitSize \*= 2) {
 // iteratively split and merge
 ListNode \*start = dummyHead.next;
 ListNode \*mergedTail = &dummyHead;
 while (start != nullptr) {
 // split by specified size
 // save second list head as well as the next sublist head
 splitHelper = split(start, splitSize, splitHelper);
 ListNode \*secondListHead = splitHelper.secondListHead;

 // merge the two sorted linked lists
 // save the head and tail of the sorted linked list
 mergeHelper = mergeTwoSortedLists(start, secondListHead, mergeHelper);
 // join two sorted lists for future iterations
 mergedTail->next = mergeHelper.mergedHead;
 // advance to the next merged tail
 mergedTail = mergeHelper.mergedTail;

 // advance to the next sublist
 start = splitHelper.nextSubListHead;
 }
 }
 return dummyHead.next;
 }
};

```

#### Java

```
/\*\*
 \* 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; }
 \* }
 \*/
class Solution {

 private static class SplitHelper {
 public ListNode secondListHead = null;
 public ListNode nextSubListHead = null;
 };

 private static class MergeHelper {
 public ListNode mergedHead = null;
 public ListNode mergedTail = null;
 };

 private static int countTotalNodes(ListNode head) {
 int result = 0;
 for (ListNode curr = head; curr != null; curr = curr.next) {
 ++result;
 }
 return result;
 }

 // split a linked list by a given splitSize
 // save the first and second segments (for merging)
 // and the head of the remaining sublist (for future iterations)
 // return the splitHelper
 private static SplitHelper split(ListNode head, int splitSize,
 SplitHelper splitHelper) {
 // trivial case
 if ((head == null) || (head.next == null)) {
 splitHelper.secondListHead = null;
 splitHelper.nextSubListHead = null;
 return splitHelper;
 }

 // find the mid and tail
 // example: if splitSize == 4
 // then mid is index 3 from head, tail is index 7 from head
 ListNode slow = head, fast = head.next;
 for (int i = 0; i < splitSize - 1; ++i) {
 slow = (slow != null) ? slow.next : null;
 for (int j = 0; j < 2; ++j) {
 fast = (fast != null) ? fast.next : null;
 }
 }

 // break the linked list at specified points
 // also save the key nodes into splitHelper
 ListNode mid = slow, tail = fast;
 splitHelper.secondListHead = (mid != null) ? mid.next : null;
 if (mid != null) {
 mid.next = null;
 }
 splitHelper.nextSubListHead = (tail != null) ? tail.next : null;
 if (tail != null) {
 tail.next = null;
 }

 return splitHelper;
 }

 // merge two sorted linked lists
 // save the merged head (for joining with the previous merged list)
 // save the merged tail (for joining with the next merged list)
 // return the mergeHelper
 private static MergeHelper mergeTwoSortedLists(ListNode head1, ListNode head2,
 MergeHelper mergeHelper) {

 ListNode dummyHead = new ListNode(0), mergedTail = dummyHead;
 ListNode p1 = head1, p2 = head2;

 while ((p1 != null) || (p2 != null)) {
 if (p1 == null) {
 // if p1 is null then use p2
 mergedTail.next = p2;
 mergedTail = p2;
 p2 = p2.next;
 }
 else if (p2 == null) {
 // if p2 is null then use p1
 mergedTail.next = p1;
 mergedTail = p1;
 p1 = p1.next;
 }
 else {
 // use the smaller one
 if (p1.val < p2.val) {
 mergedTail.next = p1;
 mergedTail = p1;
 p1 = p1.next;
 }
 else {
 mergedTail.next = p2;
 mergedTail = p2;
 p2 = p2.next;
 }
 }
 }

 mergeHelper.mergedHead = dummyHead.next;
 mergeHelper.mergedTail = mergedTail;
 return mergeHelper;
 }

 public ListNode sortList(ListNode head) {
 // bottom-up approach

 // trivial cases
 if ((head == null) || (head.next == null)) {
 return head;
 }

 final int totalNodes = countTotalNodes(head);

 SplitHelper splitHelper = new SplitHelper();
 MergeHelper mergeHelper = new MergeHelper();

 ListNode dummyHead = new ListNode(0);
 dummyHead.next = head;

 // split and merge with sizes 1, 2, 4, 8, ...
 for (int splitSize = 1; splitSize < totalNodes; splitSize \*= 2) {
 // iteratively split and merge
 ListNode start = dummyHead.next;
 ListNode mergedTail = dummyHead;
 while (start != null) {
 // split by specified size
 // save second list head as well as the next sublist head
 splitHelper = split(start, splitSize, splitHelper);
 ListNode secondListHead = splitHelper.secondListHead;

 // merge the two sorted linked lists
 // save the head and tail of the sorted linked list
 mergeHelper = mergeTwoSortedLists(start, secondListHead, mergeHelper);
 // join two sorted lists for future iterations
 mergedTail.next = mergeHelper.mergedHead;
 // advance to the next merged tail
 mergedTail = mergeHelper.mergedTail;

 // advance to the next sublist
 start = splitHelper.nextSubListHead;
 }
 }
 return dummyHead.next;
 }
}

```

## See also

* Merge sort
* Merge sort an array

## External links

* Merge sort a linked list (bottom-up approach) (Archive)