持续创作,加速成长!这是我参与「掘金日新计划 · 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));
}