鏈表原地歸併排序,關鍵詞:二重循環、2倍
題目
求解
要求的常數級空間複雜度,不能用遞歸。
class Solution {
public ListNode sortList(ListNode head) {
// 計算鏈表長度
// bottom to up,循環不變量
// head 保持爲未處理的鏈表頭
int len = 0;
ListNode p = head;
while(p != null){
p = p.next;
++len;
}
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode tail = dummyHead;
ListNode left, right;
for(int step = 1; step < len; step <<= 1){
while(head != null){
left = head;
right = cut(left, step);
head = cut(right, step);
tail = merge(tail, left, right);
}
head = dummyHead.next;
tail = dummyHead;
}
return dummyHead.next;
}
private ListNode cut(ListNode start, int step){
while(start != null){
--step;
if(step == 0) break;
start = start.next;
}
if(start == null) return null;
ListNode result = start.next;
start.next = null;
return result;
}
private ListNode merge(ListNode tail, ListNode left, ListNode right){
while(left != null && right != null){
if(left.val < right.val){
tail.next = left;
left = left.next;
}else{
tail.next = right;
right = right.next;
}
tail = tail.next;
}
if(left != null){
tail.next = left;
}
if(right != null){
tail.next = right;
}
while(tail.next != null){
tail = tail.next;
}
return tail;
}
}
dummy head(頭結點)!!!(好處:把邊界情況和普通情況做統一處理)
總結:
- cut 斷鏈
- 兩路歸併
- 鏈到 dummyHead
遞歸解法,雖不合這題題意,但以後有遇到的可能:
class Solution {
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode fast = dummyHead, slow = dummyHead;// 快慢指針必須從 dummyHead 開始
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
ListNode mid = slow.next;
slow.next = null;
head = sortList(head);
mid = sortList(mid);
merge(dummyHead, head, mid);
return dummyHead.next;
}
private void merge(ListNode dummyHead, ListNode left, ListNode right){
ListNode tail = dummyHead;
while(left != null && right != null){
if(left.val < right.val){
tail.next = left;
left = left.next;
}else{
tail.next = right;
right = right.next;
}
tail = tail.next;
}
if(left != null) tail.next = left;
if(right != null) tail.next = right;
}
}
快慢指針必須從 dummyHead 開始,才能把兩結點鏈表斷鏈爲兩個一結點鏈表。