Sort a linked list in O(n log n) time using constant space complexity.
這道題是Linked List的排序,但是對空間複雜度是有要求的。
我首先把昨天學會的那個插入排序寫上了,本來以爲不會通過,沒想到竟然通過了。但是這個用時竟然有603ms,很嚇人。
class Solution {
public ListNode sortList(ListNode head) {
if(head == null) return head;
ListNode helper = new ListNode(0);
ListNode cur = head;
ListNode pre = helper;
ListNode next = null;
while(cur != null) {
next = cur.next;
while(pre.next != null && pre.next.val < cur.val) {
pre = pre.next;
}
cur.next = pre.next;
pre.next = cur;
pre = helper;
cur = next;
}
return helper.next;
}
}
然後我看了discuss,發現了點贊最多的一個回答。這個方法,用了分而治之和遞歸的思想,很多人說由於用了遞歸不滿足空間複雜度的要求。
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
// step 1. cut the list to two halves
ListNode prev = null, slow = head, fast = head;
while (fast != null && fast.next != null) {
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
prev.next = null;
// step 2. sort each half
ListNode l1 = sortList(head);
ListNode l2 = sortList(slow);
// step 3. merge l1 and l2
return merge(l1, l2);
}
ListNode merge(ListNode l1, ListNode l2) {
ListNode l = new ListNode(0), p = l;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
p.next = l1;
l1 = l1.next;
} else {
p.next = l2;
l2 = l2.next;
}
p = p.next;
}
if (l1 != null)
p.next = l1;
if (l2 != null)
p.next = l2;
return l.next;
}
}
還看到了一種解法,這個是空間複雜度爲O(1)的。
class Solution {
public ListNode sortList(ListNode head) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode [] list = new ListNode[2];
boolean done = (null == head);
// Keep partitioning our list into bigger sublists length. Starting with a size of 1 and doubling each time
for (int step = 1; !done; step *= 2) {
done = true;
ListNode prev = dummy;
ListNode remaining = prev.next;
do {
// Split off two sublists of size step
for (int i = 0; i < 2; ++i) {
list[i] = remaining;
ListNode tail = null;
for (int j = 0; j < step && null != remaining; ++j, remaining = remaining.next) {
tail = remaining;
}
// Terminate our sublist
if (null != tail) {
tail.next = null;
}
}
// We're done if these are the first two sublists in this pass and they
// encompass the entire primary list
done &= (null == remaining);
// If we have two sublists, merge them into one
if (null != list[1]) {
while (null != list[0] || null != list[1]) {
int idx = (null == list[1] || null != list[0] && list[0].val <= list[1].val) ? 0 : 1;
prev.next = list[idx];
list[idx] = list[idx].next;
prev = prev.next;
}
// Terminate our new sublist
prev.next = null;
} else {
// Only a single sublist, no need to merge, just attach to the end
prev.next = list[0];
}
} while (null != remaining);
}
return dummy.next;
}
}