【鏈表】排序鏈表

一、題目

樂扣原題:https://leetcode-cn.com/problems/sort-list/submissions/

二、轉化爲數組

/**
 * 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 (null == head) {
            return null;
        }

        // 將鏈表轉化爲數組
        List<Integer> list = new ArrayList<>();
        while (null != head) {
            list.add(head.val);
            head = head.next;
        }

        // 數組遞增排序
        Collections.sort(list);

        // 構造結果鏈表
        ListNode root = new ListNode(-1);
        ListNode cur = root;
        for (Integer val : list) {
            cur.next = new ListNode(val);
            cur = cur.next;
        }
        return root.next;
    }
}
  • 基本思路:單向鏈表相比數組,隨機訪問的難度大,因此可以考慮轉化爲數組問題解決;
  • 時間複雜度:O(nlog(n))。鏈表遍歷O(n),數組排序O(nlog(n)),結果構造O(n);
  • 空間複雜度:O(n)。存儲鏈表元素的列表O(n),構造新的鏈表O(n);

三、歸併排序

/**
 * 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 (null == head) {
            return null;
        }
        return split(head);
    }

    private ListNode split(ListNode node) {
        // 遞歸分解到null或單節點時,直接返回
        if (null == node || null == node.next) {
            return node;
        }

        // 通過快慢指針,標記鏈表的中間節點
        ListNode slow = node;
        ListNode fast = node.next;
        while (null != fast && null != fast.next) {
            slow = slow.next;
            fast = fast.next.next;
        }

        // 以中間節點將鏈表一分爲二,通過slow.next = null斷鏈
        ListNode pre = node;
        ListNode post = slow.next;
        slow.next = null;

        // 遞歸分解兩段鏈表,直至不可再分(null/單節點)
        ListNode left = split(pre);
        ListNode right = split(post);

        // 返回兩兩歸併結果
        return merge(left, right);
    }

    private ListNode merge(ListNode left, ListNode right) {
        ListNode subResult = new ListNode(-1);
        ListNode cur = subResult;
        while (null != left && null != right) {
            if (left.val <= right.val) {
                cur.next = new ListNode(left.val);
                left = left.next;
            } else {
                cur.next = new ListNode(right.val);
                right = right.next;
            }
            cur = cur.next;
        }

        if (null != left) {
            cur.next = left;
        }

        if (null != right) {
            cur.next = right;
        }
        return subResult.next;
    }
}
  • 基本思路:根據題目要求的O(nlog(n))時間複雜度,以及單向鏈表的特性,可以聯想到歸併排序;
  • 時間複雜度:O(nlog(n))。歸併排序的時間複雜度穩定爲O(nlog(n));
  • 空間複雜度:O(log(n))。體現爲遞歸過程中的棧空間消耗,即二叉樹的最小深度,空間複雜度爲O(log(n));

四、總結

  • 在單向鏈表中,經常會使用到快慢指針尋找鏈表的中點;

 

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