【leetcode-20】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. 我用了遞歸,即把當前節點插入已經排好序的鏈表中,返回鏈表頭;
  2. 網上的歸併大法:驚豔呀!!

別人的優秀代碼(還沒有學習)

[c++] 歸併排序。

由於題目要求空間複雜度是 O(1),因此不能使用遞歸。因此這裏使用 bottom-to-up 的算法來解決。

太晚了,明天講解!

ok,歸來!

bottom-to-up 的歸併思路是這樣的:先兩個兩個的 merge,完成一趟後,再 4 個4個的 merge,直到結束。舉個簡單的例子:[4,3,1,7,8,9,2,11,5,6].

step=1: (3->4)->(1->7)->(8->9)->(2->11)->(5->6)
step=2: (1->3->4->7)->(2->8->9->11)->(5->6)
step=4: (1->2->3->4->7->8->9->11)->5->6
step=8: (1->2->3->4->5->6->7->8->9->11)
鏈表裏操作最難掌握的應該就是各種斷鏈啊,然後再掛接啊。在這裏,我們主要用到鏈表操作的兩個技術:

merge(l1, l2),雙路歸併,我相信這個操作大家已經非常熟練的,就不做介紹了。
cut(l, n),可能有些同學沒有聽說過,它其實就是一種 split 操作,即斷鏈操作。不過我感覺使用 cut 更準確一些,它表示,將鏈表 l 切掉前 n 個節點,並返回後半部分的鏈表頭。
額外再補充一個 dummyHead 大法,已經講過無數次了,仔細體會吧。

希望同學們能把雙路歸併和 cut 斷鏈的代碼爛記於心,以後看到類似的題目能夠刷到手軟。

掌握了這三大神器後,我們的 bottom-to-up 算法僞代碼就十分清晰了:

current = dummy.next;
tail = dummy;
for (step = 1; step < length; step *= 2) {
	while (current) {
		// left->@->@->@->@->@->@->null
		left = current;

		// left->@->@->null   right->@->@->@->@->null
		right = cut(current, step); // 將 current 切掉前 step 個頭切下來。

		// left->@->@->null   right->@->@->null   current->@->@->null
		current = cut(right, step); // 將 right 切掉前 step 個頭切下來。
		
		// dummy.next -> @->@->@->@->null,最後一個節點是 tail,始終記錄
		//                        ^
		//                        tail
		tail.next = merge(left, right);
		while (tail->next) tail = tail->next; // 保持 tail 爲尾部
	}
}

好了,下面是比較正式的代碼。


class Solution {
public:
    ListNode* sortList(ListNode* head) {
        ListNode dummyHead(0);
        dummyHead.next = head;
        auto p = head;
        int length = 0;
        while (p) {
            ++length;
            p = p->next;
        }
        
        for (int size = 1; size < length; size <<= 1) {
            auto cur = dummyHead.next;
            auto tail = &dummyHead;
            
            while (cur) {
                auto left = cur;
                auto right = cut(left, size); // left->@->@ right->@->@->@...
                cur = cut(right, size); // left->@->@ right->@->@  cur->@->...
                
                tail->next = merge(left, right);
                while (tail->next) {
                    tail = tail->next;
                }
            }
        }
        return dummyHead.next;
    }
    
    ListNode* cut(ListNode* head, int n) {
        auto p = head;
        while (--n && p) {
            p = p->next;
        }
        
        if (!p) return nullptr;
        
        auto next = p->next;
        p->next = nullptr;
        return next;
    }
    
    ListNode* merge(ListNode* l1, ListNode* l2) {
        ListNode dummyHead(0);
        auto p = &dummyHead;
        while (l1 && l2) {
            if (l1->val < l2->val) {
                p->next = l1;
                p = l1;
                l1 = l1->next;       
            } else {
                p->next = l2;
                p = l2;
                l2 = l2->next;
            }
        }
        p->next = l1 ? l1 : l2;
        return dummyHead.next;
    }
};

代碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode newHead = sortList(head.next), item = newHead;
        if(newHead != null && newHead.val > head.val) {
            head.next = newHead;
            return head;
        }
        while(item.next != null && item.next.val < head.val){
            item = item.next;
        }
        ListNode temp = item.next;
        item.next = head;
        head.next = temp;
        return newHead;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章