【从零开始刷leetcode】排序链表 – 归并排序初登场!

题目 148. 排序链表

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

示例 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

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

解法一 归并排序 (辅助链表)

归并排序(Merge Sort)是运用分治法的排序算法。

归并排序的核心算法是归并算法,用于将两个有序的数组组合成一个有序的数组。步骤:

  1. 初始化两个指针,分别指向两个有序数组的起始位置。
  2. 比较两个指针所指的元素,将较小的元素加入结果数组,并移动对应的指针。
  3. 重复这个过程,直到某一个数组的所有元素都被合并。
  4. 将剩余数组的元素直接添加到结果数组中,因为剩下的部分已经是有序的。

可以看出这种有序合并算法需要额外的数组空间——一个结果数组。

时间复杂度为 O(n1 + n2), 空间复杂度为 O(n1 + n2)

归并排序利用分治法,将要排序的数组分成较小的两个子数组分别排序(用递归),然后再将排序好的子数组合并成一个有序的大数组。具体排序示例如下:

class Solution {
    // 归并排序
    public ListNode sortList(ListNode head) {
        // 结束条件
        if (head == null || head.next == null) return head;
        // 从中间裁链
        ListNode slow = head, fast = head.next;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode temp = slow.next;
        slow.next = null;
        ListNode right = sortList(temp), left = sortList(head);
        ListNode signNode = new ListNode(0), preResult = signNode;
        while (left != null || right != null) { 
            if (left.val > right.val) {
                signNode.next = new ListNode(right.val);
                right = right.next;
            } else {
                signNode.next = new ListNode(left.val);
                left = left.next;
            }
            signNode = signNode.next;
        }
        signNode.next = left != null ? left : right;
        return preResult.next;
    }
}

时间复杂度:O(nlogn)

空间复杂度:O(nlogn),因为需要 logn 层递归,每层递归需要长度为 n 的辅助链表。如果要 O(1) 级别的空间复杂度,就不能用归并排序。

解法二(改进) 不用辅助链表,而是在原本链表上合并

思路与解法一一致,区别只是在不借助辅助链表,而是直接在原链表上合并,将空间复杂度降为O(1)。

class Solution {
    // 归并排序
    public ListNode sortList(ListNode head) {
        // 结束条件
        if (head == null || head.next == null) return head;
        // 从中间裁链
        ListNode slow = head, fast = head.next;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode temp = slow.next;
        slow.next = null;
        ListNode right = sortList(temp), left = sortList(head), signNode, resultNode;
        if (left.val > right.val) {
            signNode = left.val > right.val ? right : left;
            resultNode = left.val > right.val ? right : left;
            right = right.next;
        } else {
            signNode = left;
            resultNode = left;
            left = left.next;
        }
        while (left != null && right != null) {     
            if (left.val > right.val) {
                signNode.next = right;
                signNode = signNode.next;
                right = right.next;
            } else {
                signNode.next = left;
                signNode = signNode.next;
                left = left.next;
            }
            signNode.next = null;
        }
        signNode.next = left != null ? left : right;
        return resultNode;
    }
}

小结

通过今天的题目学习了归并排序,要求O(nlogn)的时间复杂度,可以联想到和树结构有关,而树结构大多与“二分”、“分治”的思想有关,自然联想到分治排序的代表——归并排序!这种分治也是递归法的的一大用武之地!

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇