Firstly, merge sort requires a helper method, which is used to merge two sorted linked lists.
Merge sorting is done half and half, so we need to find the midpoint. The most common method is to find the midpoint with fast and slow pointer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// add a dummy pointer to the start // so that in the end, the slow pointer always point to the last element of left half ListNode dummy = new ListNode(0); dummy.next = node;
// use fast and slow pointer ListNode slow = dummy; ListNode fast = dummy;
// split the list while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; }
In the above code, there is a dummy node. When the number of nodes is even, then slow just point to the last node of the previous half of the node, which is the state below.
1 2 3
1 2 3 4 ^ ^ slow fast
If both slow and fast start from the head, then when fast ends, slow will go to the beginning of the next half of the node.
// helper method to split original list into 2 half // recursive the left and right half // merge the left and right half privatestatic ListNode mergeSort(ListNode node){ // reach the end if (node == null || node.next == null) { return node; }
// add a dummy pointer to the start // so that in the end, the slow pointer always point to the last element of left half ListNode dummy = new ListNode(0); dummy.next = node;
// use fast and slow pointer ListNode slow = dummy; ListNode fast = dummy;
// split the list while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; }
// slow is the last node of left half ListNode rightHalf = slow.next;
// make slow.next = null so we have 2 split list slow.next = null;
// recursive sort the left and right half ListNode head1 = mergeSort(node); ListNode right = mergeSort(rightHalf);
return merge(head1, right); }
// helper method to merge 2 sorted list privatestatic ListNode merge(ListNode head1, ListNode head2){ ListNode dummyHead = new ListNode(0); ListNode lastNode = dummyHead;
while (head1 != null && head2 != null) { if (head1.val < head2.val) { // head1 is smaller // add head1 to the last // move head1 to next lastNode.next = head1; lastNode = lastNode.next; head1 = head1.next; } else { lastNode.next = head2; lastNode = lastNode.next; head2 = head2.next; } }
// we exit the while loop because one of the head1 or head2 reach end // append the other one to the last if (head1 != null) { lastNode.next = head1; }
if (head2 != null) { lastNode.next = head2; }
return dummyHead.next; }
Of course, strictly speaking, the space complexity of the solution above is not O(1), because pushing the stack in the recursive process consumes space, and takes half each time, so the space complexity is O(logn).