Problem#
Given a Linked List, Sort it using merge sort.
Examples#
Example 1:
1
2
|
Input: head = [4,2,1,3]
Output: [1,2,3,4]
|
Solution#
Method 1 - Recursive#
Refer merge sort - Merge Sort Algorithm.
- Get the pointer to middle node
- Split the list at the middle node
- Continue 1 and 2 , till list size is 1 or 2.
- Sort the elements in step 3
- Merge the sorted list
Now this involves 2 problems - Middle of the linked list and Merge Two Sorted Linked Lists.
To split linked list into 2, we can also use: Split Linked List into two halves fractionally given n
Code#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode left = head;
// note we are not getting mid node, but pointer to mid node
ListNode prevToMid = getPointerToMidNode(head);
ListNode right = prevToMid.next;
prevToMid.next = null;
left = sortList(left);
right = sortList(right);
return mergeIterative(left, right);
}
public ListNode getPointerToMidNode(ListNode head) {
ListNode fast = head, slow = head, prev = null;
while (fast != null && fast.next != null) {
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
return prev;
}
public ListNode mergeIterative(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode curr = dummyHead;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
curr.next = l1;
l1 = l1.next;
} else {
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
curr.next = l1 == null ? l2 : l1;
return dummyHead.next;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
class Solution:
class ListNode:
def __init__(self, val: int = 0, next: Optional['Solution.ListNode'] = None):
self.val = val
self.next = next
def sortList(self, head: Optional['Solution.ListNode']) -> Optional['Solution.ListNode']:
if not head or not head.next:
return head
left = head
prev_to_mid = self.get_pointer_to_mid_node(head)
right = prev_to_mid.next
prev_to_mid.next = None
left = self.sortList(left)
right = self.sortList(right)
return self.merge_iterative(left, right)
def get_pointer_to_mid_node(self, head: 'Solution.ListNode') -> 'Solution.ListNode':
fast, slow, prev = head, head, None
while fast and fast.next:
prev = slow
slow = slow.next
fast = fast.next.next
return prev
def merge_iterative(self, l1: Optional['Solution.ListNode'], l2: Optional['Solution.ListNode']) -> Optional['Solution.ListNode']:
dummy_head = self.ListNode(0)
curr = dummy_head
while l1 and l2:
if l1.val < l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
curr.next = l1 if l1 else l2
return dummy_head.next
|
Complexity#
- ⏰ Time complexity:
O(n log n)
because of the divide-and-conquer approach of merge sort.
- 🧺 Space complexity:
O(1)
because the sorting is done in place.