leetcode 148. 排序鏈表

鏈表原地歸併排序,關鍵詞:二重循環、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(頭結點)!!!(好處:把邊界情況和普通情況做統一處理)
總結:

  1. cut 斷鏈
  2. 兩路歸併
  3. 鏈到 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 開始,才能把兩結點鏈表斷鏈爲兩個一結點鏈表。

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