Analysis
This question requires to sort a LinkedList in O(nlogn)
time and O(1)
space. We know that all comparison-based sorting alrogithm have the upperbound time complexity of O(nlogn)
. Thus it is obvious that we are looking for some transformation of one of the following algorithms:
Merge sort, Quick sort, Heap sort.
Considering we are dealing with a LinkedList and can only use O(1)
space, we need to think of which one of the above algorithm can be applied to this situation.
Heap sort is apparently not applicable because of its space complexity.
Quick sort is applicable since, we just need to modify the corresponding partition process of quick sort.
A quick sort implmentation is showed as following.
Note that an important difference of this quick sort with a normal array quick sort is that we have to merge the resulting sub-LinkedList.
class Solution {
public ListNode sortList(ListNode head) {
// When the LinkedList is null or has a length of 1, no need to sort.
if (head == null || head.next == null) {
return head;
}
return quickSort(head);
}
private ListNode quickSort(ListNode head) {
// Base case
if (head == null || head.next == null) {
return head;
}
ListNode fakeSmall = new ListNode(0);
ListNode small = fakeSmall;
ListNode fakeEqual = new ListNode(0);
ListNode equal = fakeEqual;
ListNode fakeLarge = new ListNode(0);
ListNode large = fakeLarge;
// use head as pivot
ListNode curr = head;
while (curr != null) {
if (curr.val > head.val) {
large.next = curr;
large = large.next;
} else if (curr.val < head.val) {
small.next = curr;
small = small.next;
} else {
equal.next = curr;
equal = equal.next;
}
curr = curr.next;
}
small.next = null;
large.next = null;
equal.next = null;
return merge(merge(quickSort(fakeLarge.next), quickSort(fakeSmall.next)), fakeEqual.next);
}
Merge sort is also applicable, we need to change the process of finding middle point and merge. The merge is just the same as Merge 2 sorted LinkedList.
A merge sort implementation is showed as following:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
// When the LinkedList is null or has a length of 1, no need to sort.
if (head == null || head.next == null) {
return head;
}
return mergeSort(head);
}
private ListNode mergeSort(ListNode head) {
/**
* Using merge sort to sort the 2 LinkedList recursively.
*/
if (head.next == null) {
return head;
}
// Get the middle point of the LinkedList
ListNode middle = findMiddle(head);
// Sort the first half
ListNode left = mergeSort(head);
// Sort the second half
ListNode right = mergeSort(middle);
return merge(left, right);
}
private ListNode merge(ListNode left, ListNode right) {
// Merge 2 sorted LinkedList
ListNode dummy = new ListNode(0);
ListNode head = dummy;
while (left != null && right != null) {
if (left.val <= right.val) {
head.next = left;
left = left.next;
} else {
head.next = right;
right = right.next;
}
head = head.next;
}
if (left != null) {
head.next = left;
}
if (right != null) {
head.next = right;
}
return dummy.next;
}
private ListNode findMiddle(ListNode head) {
/**
* Find the middle point of a linked list
* And split the original LinkedList into 2 separated List by removing
* the next pointer in the middle.
*/
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode ret = slow.next;
slow.next = null;
return ret;
}
}