【第五周】排序链表

LeetCode 148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

进阶:

  • 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

示例1:

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 104]
  • -105 <= Node.val <= 105

归并排序

自顶向下归并排序(递归)

class Solution {
    public ListNode sortList(ListNode head) {
        /* * * * * * * * * * * *
         *  使用递归的归并排序  *
         * * * * * * * * * * * */
        if (head == null || head.next == null) return head;

        // 寻找中点,节点为奇数个时为中点,节点为偶数个时为左侧中间点
        ListNode fast = head.next, slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }

        ListNode temp = slow.next;
        slow.next = null;
        // 归并
        ListNode left = sortList(head);
        ListNode right = sortList(temp);
        ListNode result = merge(left, right);
        return result;
    }

    // 按升序合并两个链表(LC 21.合并两个有序链表)
    private ListNode merge(ListNode left, ListNode right) {
        ListNode dummyNode = new ListNode(0);
        ListNode l = left, r = right, temp = dummyNode;

        while (l != null && r != null) {
            if (l.val <= r.val) {
                temp.next = l;
                l = l.next;
            } else {
                temp.next = r;
                r = r.next;
            }
            temp = temp.next;
        }
        if (l != null) {
            temp.next = l;
        } else if (r != null) {
            temp.next = r;
        }
        return dummyNode.next;
    }
}
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(logn),递归调用的栈空间。

自底向上归并排序(非递归)

  • 遍历链表,计算总长度length,用 subLength 表示每次需要排序的子链表的长度,初始时 subLength=1。
  • 每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于 subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。
  • 将 subLength 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 length,整个链表排序完毕。
class Solution {
    public ListNode sortList(ListNode head) {
        /* * * * * * * * * * * *
         * 不使用递归的归并排序 *
         * * * * * * * * * * * */
        if (head == null || head.next == null) return head;
        int length = 0;
        ListNode node = head;
        // 统计链表长度
        while (node != null) {
            length++;
            node = node.next;
        }

        ListNode dummyNode = new ListNode(0, head);
        for (int subLength = 1; subLength < length; subLength *= 2) {
            ListNode prev = dummyNode, curr = dummyNode.next;
            while (curr != null) {
                ListNode left = curr;
                for (int i = 1; i < subLength && curr.next != null; i++) {
                    curr = curr.next;
                }
                ListNode right = curr.next;
                curr.next = null;
                curr = right;
                for (int i = 1; i < subLength && curr != null && curr.next != null; i++) {
                    curr = curr.next;
                }
                ListNode next = null;
                if (curr != null) {
                    next = curr.next;
                    curr.next = null;
                }

                ListNode merged = merge(left, right);
                prev.next = merged;
                while (prev.next != null) {
                    prev = prev.next;
                }
                curr = next;
            }
        }
        return dummyNode.next;
    }
}
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

你可能感兴趣的:(【第五周】排序链表)