題目描述
在 O(n log n) 時間複雜度和常數級空間複雜度下,對鏈表進行排序。
示例 1:
輸入: 4->2->1->3
輸出: 1->2->3->4
示例 2:
輸入: -1->5->3->4->0
輸出: -1->0->3->4->5
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sort-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
思路
看到時間複雜度就想到了快排和歸併排序
但是常數級空間複雜度 很明顯不能遞歸調用,那就有點複雜。 先從歸併排序的遞歸開始寫起。
- 首先需要找到中間節點,然後cut,切斷爲兩個鏈表,然後遞歸調用切斷函數,直到最後變成兩個單個節點的鏈表,依次排序每一輪遞歸的鏈表,再合併。
public static ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
//快慢指針
ListNode fast = head.next, slow = head;
//找到中間節點
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
//tmp存儲後半部分節點
ListNode tmp = slow.next;
//將前半部分節點的末尾置爲null
slow.next = null;
//遞歸調用
ListNode left = sortList(head);
ListNode right = sortList(tmp);
//創建前綴鏈表
ListNode h = new ListNode(0);
ListNode res = h;
//拼接鏈表
while (left != null && right != null) {
if (left.val < right.val) {
h.next = left;
left = left.next;
} else {
h.next = right;
right = right.next;
}
h = h.next;
}
h.next = left != null ? left : right;
return res.next;
}
易於理解的版本
public ListNode sortListDemo(ListNode head) {
return mergeSort(head);
}
private ListNode mergeSort(ListNode head){
//如果只有一個節點,或者節點爲空,直接返回
if(head == null || head.next == null)
return head;
ListNode slow = head;
ListNode fast = head.next.next;
ListNode l, r;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
//對右半部分進行排序
r = mergeSort(slow.next);
slow.next = null;
//對左半部分進行排序
l = mergeSort(head);
return(mergeList(l, r));
}
private ListNode mergeList(ListNode l,ListNode r){
ListNode tmp = new ListNode(0);
ListNode res = tmp;
//不斷根據順序拼接鏈表
for(l != null && r != null) {
if(l.val > r.val){
res.next = r;
r = r.next;
}else{
res.next = l;
l = l.next;
}
res = res.next;
}
//某一個鏈表不爲空,直接拼接在後方
res.next = (l == null)? r : l;
return tmp.next;
}
最後一個不使用遞歸的辦法
//不使用遞歸
public ListNode sortListDemo2(ListNode head) {
if(head == null || head.next == null) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
int len = getListLen(head);
int itrv = 1;
while(itrv < len) {
ListNode pre = dummy;
ListNode h = dummy.next;
// 找到合併鏈表的h1和h2頭節點
while(h!=null) {
int i = itrv;
ListNode h1 = h;
for(; h != null && i > 0; i--) {
h = h.next;
}
// i>0說明沒有鏈表2直接返回
if(i > 0) break;
ListNode h2 = h;
i = itrv;
for(; h != null && i > 0; i--) {
h = h.next;
}
// 求出兩個鏈表的長度
int c1 = itrv;
int c2 = itrv - i;
//合併
while(c1 > 0 && c2 > 0) {
if(h1.val < h2.val) {
pre.next = h1;
h1 = h1.next;
c1--;
}else {
pre.next = h2;
h2 = h2.next;
c2--;
}
pre = pre.next;
}
pre.next = c1 > 0 ? h1 : h2;
while(c1 > 0 || c2 > 0) {
pre = pre.next;
c1--;
c2--;
}
pre.next = h;
}
itrv*=2;
}
return dummy.next;
link
這不是我自己寫的 感覺自己還沒想通,所以貼了別人實現的代碼。 還得好好看一下 繼續努力吧