运行结果
执行结果:通过
执行用时 :993 ms, 在所有 Java 提交中击败了5.82%的用户
内存消耗 :39.8 MB, 在所有 Java 提交中击败了98.65%的用户
代码与注释
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
// 归并排序
class Solution {
public ListNode sortList(ListNode head) {
// 如果链表为null直接返回null。
if (head == null) return head;
// 否则返回归并排序后结果
return mergeSort(head);
}
// 第一步:递归分割
public ListNode mergeSort(ListNode head)
{
// 基线条件
if (head.next == null) return head;
// 递归将现有链表划分为子链表
ListNode p1 = head; // 左指针
ListNode p2 = head.next; // 右指针
// 找到链表的中点
if (p2 != null && p2.next != null)
{
p1 = p1.next;
p2 = p2.next.next;
}
ListNode left = head; // 左链表的头节点
ListNode right = p1.next; // 右链表的头节点
p1.next = null; // 断开链表中间点
left = mergeSort(left);
right = mergeSort(right);
return merge(left, right);
}
// 第二步:归并
public ListNode merge(ListNode left, ListNode right)
{
// 返回用头结点
ListNode head = null;
// 找到头结点
if (left.val < right.val)
{
head = left;
left = left.next;
}
else
{
head = right;
right = right.next;
}
ListNode tmp = head;
// 两边都不到链表尾
while (left != null && right != null)
{
if (left.val < right.val)
{
tmp.next = left;
left = left.next;
}
else
{
tmp.next = right;
right = right.next;
}
tmp = tmp.next;
}
// 某一边已经到链表尾
tmp.next = left != null ? left : right;
return head;
}
}
做题体会
- 题目要求时间复杂度为O(nlogn),空间复杂度为常数阶O(1),上述解答的空间复杂度不满足要求,使用的递归分解,空间复杂度为O(logn)。
- 用归并法排序,主要思想分为两步:分割和归并。分割:把待排序数据分割为两个部分,对两个子部分再做同样的分割,直至不能再划分为止。归并:通过比较,将子部分依次归并为一个大的有序部分,再对较大的有序部分执行同样的操作直至整个数据有序。
- 分割采用快慢指针法,快指针到达链表末尾时,慢指针到达链表的中部,通过慢指针指向的下一个节点为null,断开链表为两个部分,需要注意的是,初始化快指针为head.next,是为了防止只有两个节点的链表发生分割错误。
- 在归并时,当某一边的链表已经到达尾部,只需要将结果链表指向另一边链表的头部即可,无需再做循环判断。