1. 程式人生 > >148. 排序鏈表

148. 排序鏈表

return tlist 時間復雜度 並排 返回 listnode 數組 輸入 link

題目描述

O(n log n) 時間復雜度和常數級空間復雜度下,對鏈表進行排序。

示例 1:

輸入: 4->2->1->3
輸出: 1->2->3->4

示例 2:

輸入: -1->5->3->4->0
輸出: -1->0->3->4->5

分析

要保證時間復雜度為 O(n log n) ,所以想到了歸並排序和快排,但是這兩個都是針對數組的,用鏈表來實現就有點難了。
歸並排序法:在動手之前一直覺得空間復雜度為常量不太可能,因為原來使用歸並時,都是 O(N)的,需要復制出相等的空間來進行賦值歸並。對於鏈表,實際上是可以實現常數空間占用的(鏈表的歸並排序不需要額外的空間)。利用歸並的思想,遞歸地將當前鏈表分為兩段,然後merge,分兩段的方法是使用 fast-slow 法,用兩個指針,一個每次走兩步,一個走一步,知道快的走到了末尾,然後慢的所在位置就是中間位置,這樣就分成了兩段。merge時,把兩段頭部節點值比較,用一個 p 指向較小的,且記錄第一個節點,然後 兩段的頭一步一步向後走,p也一直向後走,總是指向較小節點,直至其中一個頭為NULL,處理剩下的元素。最後返回記錄的頭即可。

主要考察3個知識點,
知識點1:歸並排序的整體思想
知識點2:找到一個鏈表的中間節點的方法
知識點3:合並兩個已排好序的鏈表為一個新的有序鏈表

貼出代碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        return head == null ? null : mergeSort(head);
    }
    
    private ListNode mergeSort(ListNode head){
        if(head.next == null){
            return head;
        }
        ListNode p = head, q = head, pre = null;
        while(q != null && q.next != null){
            pre = p;
            p = p.next;
            q = q.next.next;
        }
        pre.next = null;
        ListNode l = mergeSort(head);
        ListNode r = mergeSort(p);
        return merge(l,r);
    }
    
    private ListNode merge(ListNode l, ListNode r){
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        while(l != null && r != null){
            if(l.val <= r.val){
                curr.next = l;
                curr = curr.next;
                l = l.next;
            }else{
                curr.next = r;
                curr = curr.next;
                r = r.next;
            }
        }
        if(l != null){
            curr.next = l;
        }
        if(r != null){
            curr.next = r;
        }
        return dummy.next;
    }
}

148. 排序鏈表