1、描述
在O(nlogn)时间复杂度和常数级空间复杂度下,对链表进行排序。
例1:输入:4 -> 2 -> 1-> 3
输出:1 -> 2 -> 3 -> 4
例2:输入:-1 -> 5 -> 3 ->4 -> 0
输出:-1 -> 0 -> 3 -> 4 -> 5
2、算法
思想:对并排序
由于题目要求时间空间复杂度分别为O(nlogn)和O(1),根据时间复杂度我们自然想到二分法,从而联想到归并排序。
1、递归使用二分法将链表分割成两个链表
1)不过由于是单向链表,没法直接获得中间节点,需要循环先计算出链表的长度。
2)通过 count/2 计算出中间的节点,注意分割后的链表的长度为count/2和(count-count/2),考虑奇数问题
3)找到中间节点后,把链表断成两个链表,不然各种判断会很复杂
2、合并有序的链表
1)第一步分到最后是单个元素的链表,可以看成有序链表
2)实际上,这一步就转换成了合并两个有序的链表
3)使用递归,每次只判断链表头,代码简洁且易懂
func sortList(_ head : ListNode?)->ListNode?{
if head == nil || head?.next == nil {
return head
}
//计算出链表的长度
var countNode = head
var count = 0
while countNode != nil {
count += 1
countNode = countNode?.next
}
return sortList(head, count)
}
//排序链表
private func sortList(_ head : ListNode?, _ count : Int)->ListNode?{
//递归结束条件
if count <= 1{
return head
}
var leftEnd = head
for i in 0..<count/2-1{
leftEnd = leftEnd?.next
}
var rightStart = leftEnd?.next
//断链,如果不断链,各种判断让你死去活来
leftEnd?.next = nil
//合并两个已经排好序的链表
//第二个链表的长度为count - count / 2,不能直接是count / 2,奇数计算会错误
return merge(sortList(head, count/2), sortList(rightStart, count-count/2))
}
//合并两个有序的链表,使用递归更简洁,每次只比较两个链表的链头
private func merge(_ l1 : ListNode?, _ l2 : ListNode?)->ListNode?{
if l1 == nil {
return l2
}
if l2 == nil {
return l1
}
var l1 = l1
var l2 = l2
var head : ListNode?
if l1!.val <= l2!.val {
head = l1
l1?.next = merge(l1?.next, l2)
}else{
head = l2
l2?.next = merge(l1, l2?.next)
}
return head
}