给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
思路与代码
归并排序 递归
时间复杂度O(nlog(n))
空间复杂度O(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; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
// 1、递归结束条件
if (head == null || head.next == null) {
return head;
}
// 2、找到链表中间节点并断开链表 & 递归下探
ListNode middleHead = middleHead(head);
ListNode rightHead = middleHead.next;
middleHead.next = null;
ListNode left = sortList(head);
ListNode right = sortList(rightHead);
// 3、当前层业务操作(合并有序链表)
return mergeTwoLists(left, right);
}
// 找到链表中间节点(876. 链表的中间结点 如果有两个中间节点,就找到前一个)
private ListNode middleHead(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
// 合并两个有序链表(21. 合并两个有序链表)
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode sentine = new ListNode(-1);
ListNode curNode = sentine;
while (l1 != null && l2 != null){
curNode.next = l1.val <= l2.val? l1 : l2;
curNode = curNode.next;
if (l1.val <= l2.val) l1 = l1.next;
else l2 = l2.next;
}
curNode.next = l1 != null ? l1 : l2;
return sentine.next;
}
}
迭代归并法
bottom-to-up 的归并思路是这样的:先两个两个的 merge,完成一趟后,再 4 个4个的 merge,直到结束。举个简单的例子:[4,3,1,7,8,9,2,11,5,6]
.
step=1: (3->4)->(1->7)->(8->9)->(2->11)->(5->6)
step=2: (1->3->4->7)->(2->8->9->11)->(5->6)
step=4: (1->2->3->4->7->8->9->11)->5->6
step=8: (1->2->3->4->5->6->7->8->9->11)
链表里操作最难掌握的应该就是各种断链啊,然后再挂接啊。在这里,我们主要用到链表操作的两个技术:
merge(l1, l2)
,双路归并,我相信这个操作大家已经非常熟练的,就不做介绍了。cut(l, n)
,可能有些同学没有听说过,它其实就是一种 split 操作,即断链操作。不过我感觉使用 cut 更准确一些,它表示,将链表 l 切掉前 n 个节点,并返回后半部分的链表头。- 额外再补充一个
dummyHead
大法,已经讲过无数次了,仔细体会吧。
该思路来自LeetCode
作者:ivan_allen
链接:https://leetcode-cn.com/problems/sort-list/solution/148-pai-xu-lian-biao-bottom-to-up-o1-kong-jian-by-/
来源:力扣(LeetCode)
时间复杂度O(nlog(n))
空间复杂度O(1)
/**
* 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; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode tmp = head;
int len = 0;
while (tmp != null){
tmp = tmp.next;
len++;
}
ListNode dummy = new ListNode();
dummy.next = head;
for (int size = 1; size < len; size *= 2) {
ListNode cur = dummy.next;
ListNode tail = dummy;
while (cur != null) {
ListNode left = cur;
ListNode right = cut(left, size);
cur = cut(right, size);
tail.next = mergeTwoLists(left, right);
while (tail.next != null) tail = tail.next;
}
}
return dummy.next;
}
public ListNode cut(ListNode node, int size) {
while (node != null) {
if (size == 1) {
ListNode res = node.next;
node.next = null;
return res;
}
node = node.next;
size--;
}
return null;
}
// 合并两个有序链表(21. 合并两个有序链表)
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode sentine = new ListNode(-1);
ListNode curNode = sentine;
while (l1 != null && l2 != null){
curNode.next = l1.val <= l2.val? l1 : l2;
curNode = curNode.next;
if (l1.val <= l2.val) l1 = l1.next;
else l2 = l2.next;
}
curNode.next = l1 != null ? l1 : l2;
return sentine.next;
}
}