题目描述

// 148. 排序链表
// 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
// 进阶:
// 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
题解
/**
* 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; }
* }
*/
// 求链表中点 + 合并排序链表
// 这道题需要结合一下【Leetcode】876. 链表的中间结点 和
// 【剑指offer】25. 合并两个排序的链表 这两道题的方法。
//
// 我们的思路是将链表按照归并排序的思路,将链表拆成left和right左右两半,
// 再将left和right左右两半各自拆成左右两半,一直拆解到left只有一个结点,
// right也只有一个结点。我们用 25. 合并两个排序的链表 的方法,将它们合并
// 得到排序的小链表,两个排序的小链表合并成两个排序的大链表,两个排序
// 的大链表最后层层合并,就合并回了原链表,我们就得到了排序好的链表。
// 首先定义middleNode函数,
// 我们需要拆链表为left right左右两半,就需要取链表中点,
// 这里和【Leetcode】876. 链表的中间结点基本一样,有一个小trick。
// 由于我们不只是需要找中点,还需要把left和right拆开。而【Leetcode】876
// 中找到的是链表中点(奇数个元素)或者链表中间位置靠右的点(偶数个元素)
// 。我们需要切断链表left和right,需要找链表中点的前一个点middlePre,然后
// 令middlePre = null就切断了left和right。为了找到链表中点的前一个点,
// 我们将快慢针的快针初始化到head.next的位置,然后再快慢针遍历,这样慢针
// 找到的就是中点的前一个点。
//
// 定义mergeNodeList函数,
// 这里和【剑指offer】25. 合并两个排序的链表 一模一样的。
//
// 最后处理主函数sortList,
// sortList的功能是将输入的链表head拆分为left和right左右两半,然后各自将
// left和right进行排序,再将left和right合并。
// 我们定义递归终止条件为head为null或者head.next为null,则直接返回head。
// 因为如果head为null或者只有一个结点,那就无法拆分为左右两半。
// 然后调用middleNode找head的中点的前一个点middlePre,我们记录middlePre
// 的下一个点作为右半部分链表的头结点rightHead,然后将左右切断,即
// middlePre.next = null。这样就切断了left和right,然后我们将left递归
// 调用sortList,将left也要拆分排序后归并。将right递归调用sortList,
// 将right也要拆分排序后归并。
// 最后函数返回:合并两个排序的左右子链表即可,mergeNodeList(left, right)。
//
// 执行用时:6 ms, 在所有 Java 提交中击败了98.94%的用户
// 内存消耗:42.9 MB, 在所有 Java 提交中击败了97.93%的用户
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode middlePre = middleNode(head);
ListNode rightHead = middlePre.next;
middlePre.next = null;
ListNode left = sortList(head);
ListNode right = sortList(rightHead);
return mergeNodeList(left, right);
}
public ListNode middleNode(ListNode head) {
if (head == null)
return null;
ListNode cur = head.next;
ListNode pre = head;
while (cur != null && cur.next != null) {
cur = cur.next.next;
pre = pre.next;
}
return pre;
}
public ListNode mergeNodeList(ListNode l1, ListNode l2) {
if (l1 == null)
return l2;
else if (l2 == null)
return l1;
ListNode res;
ListNode temp;
res = (l1.val <= l2.val) ? l1 : l2;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
while (l1.next != null && l1.next.val <= l2.val)
l1 = l1.next;
temp = l1;
l1 = l1.next;
temp.next = l2;
}
else {
while (l2.next != null && l2.next.val < l1.val)
l2 = l2.next;
temp = l2;
l2 = l2.next;
temp.next = l1;
}
}
return res;
}
}
// 执行用时:6 ms, 在所有 Java 提交中击败了98.96%的用户
// 内存消耗:46.9 MB, 在所有 Java 提交中击败了28.04%的用户
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode mid = middle(head); // 找中点,准备拆成两半
ListNode left = head; // 左半链表
ListNode right = mid.next; // 右半链表
mid.next = null; // 左右半边拆开
left = sortList(left);
right = sortList(right);
ListNode res = mergeTwoLists(left, right);
return res;
}
// 找链表中点(中点靠左)
private ListNode middle(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode pre = head;
ListNode cur = head.next;
while (cur != null && cur.next != null) {
pre = pre.next;
cur = cur.next.next;
}
return pre;
}
// 合并排序链表
private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null)
return l2;
else if (l2 == null)
return l1;
ListNode dummyHead = new ListNode(-1);
ListNode head = dummyHead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
head.next = l1;
head = l1;
l1 = l1.next;
}
else {
head.next = l2;
head = l2;
l2 = l2.next;
}
}
if (l1 != null)
head.next = l1;
else if (l2 != null)
head.next = l2;
return dummyHead.next;
}
}
////////////////////////////////////////////////////////
// 你也可以选择把所有的方法全部揉在一起。
// 执行用时:5 ms, 在所有 Java 提交中击败了99.52%的用户
// 内存消耗:43.2 MB, 在所有 Java 提交中击败了94.07%的用户
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode cur = head.next;
ListNode pre = head;
while (cur != null && cur.next != null) {
cur = cur.next.next;
pre = pre.next;
}
ListNode rightHead = pre.next;
pre.next = null;
ListNode left = sortList(head);
ListNode right = sortList(rightHead);
ListNode res;
ListNode temp;
res = (left.val <= right.val) ? left : right;
while (left != null && right != null) {
if (left.val <= right.val) {
while (left.next != null && left.next.val <= right.val)
left = left.next;
temp = left;
left = left.next;
temp.next = right;
}
else {
while (right.next != null && right.next.val < left.val)
right = right.next;
temp = right;
right = right.next;
temp.next = left;
}
}
return res;
}
}