[路飞]_LeetCode_148. 排序链表

209 阅读2分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战

题目

给你链表的头结点 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) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

来源:力扣(LeetCode)leetcode-cn.com/problems/so…

解题思路

如果需要把时间复杂度控制在 O(n log n) 下,很容易就会想到二路归并排序。

二路归并排序的实现方式:

  • 链表查找中间节点:通过双指针遍历链表,当快指针走出链表时,慢指针的当前位置就是链表的中间节点;
  • 找到中间节点后,把中间节点的下个节点存起来当作右子链表的头节点,同时把中间节点和后面的节点断开;
  • 分别对左子链接(头节点)和右子链接(上一步存起来的头节点)递归排序;
  • 递归结束条件:头节点的 next 为 null;
  • 合并左右两个子链表;

代码实现

var sortList = function (head) {
    //head.next 为空时,递归结束
    if (head === null || head.next === null) return head

    let fast = head.next
    let slow = head

    //找到链表的中间节点
    while (fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
    }

    const tmp = slow.next
    slow.next = null //从中间断开

    //递归排序左边子链表
    let left = sortList(head)
    //递归排序右边子链表
    let right = sortList(tmp)
    
    let curr = new ListNode()
    const ans = curr

    while (left && right) {
        if (left.val <= right.val) {
            //左链表表头节点小于等于右链表表头节点,把左链表头节点加入新链表
            //左链表后移
            curr.next = left
            left = left.next
        } else {
            //右链表表头节点大于左链表表头节点,把右链表头节点加入新链表
            //右节点后移
            curr.next = right
            right = right.next
        }
        
        //新链表当前节点后移
        curr = curr.next
    }

    //把左边或右边剩余的部分加到链表中
    curr.next = left ? left : right

    return ans.next
};

如有错误欢迎指出,欢迎一起讨论!