Leecode 148. 排序鏈表

題目描述

在 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
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

思路

看到時間複雜度就想到了快排和歸併排序
但是常數級空間複雜度 很明顯不能遞歸調用,那就有點複雜。 先從歸併排序的遞歸開始寫起。

  1. 首先需要找到中間節點,然後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
這不是我自己寫的 感覺自己還沒想通,所以貼了別人實現的代碼。 還得好好看一下 繼續努力吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章