题目描述:
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
提交结果
归并排序(最简单的)
虽然归并排序在对数组排序的时候空间复杂度为O(n)
,因为在merge的过程中会开辟一个长度为n的临时数组。但对两个有序链表的合并空间复杂度是O(1)
的,只需将两条链表按照次序挂在一起即可,不需要辅助空间。
核心思想
和对数组的归并排序一样,分为三个过程
- 将链表划分为两部分(二路归并)
- 然后对分成的两部分采取同递归处理,直至不能再分(即只有一个结点,或者没有结点),因为此时每部分只有一个结点或者没有结点,所以每一部分都是有序的。
- 进行有序链表的合并
注: 整个链表的排序过程以null,作为划分,而不再像数组那样有边界元素的索引作为界限。
具体操作
1.快慢指针法找中点
排序对象是链表,没有下标索引,因此需要通过遍历找中间结点,而快慢指针法只需一趟遍历即可找到中间结点。之前写得找中间结点的博客,也是LeetCode上的题目。快慢指针法找中点
2.递归调用mergeSortList
3.有序链表的合并
对于有序链表的合并,也是LeetCode上的题目,可分为递归解法和循环解法,也比较简单,可以参照之前写得合并有序链表的博客。合并有序链表.
代码实现
主体代码:
public ListNode mergeSortList(ListNode head) {
// 空链表和一个结点的链表无需排序
if(head == null || head.next == null){
return head;
}
// 找中间结点
ListNode midNode = findMid(head);
// 左部分排序
ListNode left = mergeSortList(head);
// 右部分排序
ListNode right = mergeSortList(midNode);
// 合并
ListNode newHead = merge(left,right);
return newHead;
}
快慢指针找中间结点
private ListNode findMid(ListNode head){
ListNode fast = head;
ListNode slow = head;
ListNode prev = null;
while(fast != null && fast.next != null){
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
prev.next = null; // 前半部分排序的划分界限
return slow;
}
合并两个有序链表
private ListNode merge(ListNode left,ListNode right){
if(left == null) return right;
if(right == null) return left;
ListNode head = null;
if(left.val <= right.val){
head = left;
head.next = merge(left.next,right);
}else{
head = right;
head.next = merge(left,right.next);
}
return head;
}