Leetcode 148排序链表

82 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

*1.题目

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

示例 1:

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

本题需要我们将链表按照升序排列返回,排序的方法有很多种,例如快排,插入,二分等各种各样的排序方法。而适合链表的排序方法则是归并排序,对我们来说采用递归的方法可以比较容易的实现归并排序。

我们只需要将链表不停的对半拆分,拆分到每个链表都只有一个节点的情况,然后再开始排序合并,不停的扩充链表长度,最后恢复回最开始的模样。

首先我们需要一个合并排序的方法,接收两个链表传入,首先我们创建一个合并后的链表,然后创建三个指针对应三个链表。然后开始循环两个传入的链表,比较大小之后将较小值接入新链表,并将较小链表以及新链表向后移动一位,直到有一个链表遍历完成。然后如果有链表还未遍历完则直接接入新链表后面并返回新链表。

const merge = (head1, head2) => {
    const dummyHead = new ListNode(0);
    let temp = dummyHead, temp1 = head1, temp2 = head2;
    while (temp1 !== null && temp2 !== null) {
        if (temp1.val <= temp2.val) {
            temp.next = temp1;
            temp1 = temp1.next;
        } else {
            temp.next = temp2;
            temp2 = temp2.next;
        }
        temp = temp.next;
    }
    if (temp1 !== null) {
        temp.next = temp1;
    } else if (temp2 !== null) {
        temp.next = temp2;
    }
    return dummyHead.next;
}

然后我们还需要一个拆分链表的函数,接收头节点和尾节点两个参数。首先判断当前链表是否只有一个或者没有节点。然后通过快慢指针的方法来获取链表的中间节点,即快指针走两步,慢指针走一步,然后当快指针走到结尾的适合慢指针就在中间位置。然后将链表通过中间节点拆分成两个子链表进行递归拆分,并将拆分结果调用之前的方法进行排序合并。

const toSortList = (head, tail) => {
    if (head === null) {
        return head;
    }
    if (head.next === tail) {
        head.next = null;
        return head;
    }
    let slow = head, fast = head;
    while (fast !== tail) {
        slow = slow.next;
        fast = fast.next;
        if (fast !== tail) {
            fast = fast.next;
        }
    }
    const mid = slow;
    return merge(toSortList(head, mid), toSortList(mid, tail));
}