时间复杂度:O(n logn),空间复杂度:O(1)
解题思路
一定要用空间复杂度为O(1)的方法解题,不然面试官极有可能让你优化代码!
要求时间复杂度为O(n logn),那一定就会想到二分排序法。数组中最常用的排序方法就是快速排序,但在链表中最常用的排序方法是归并排序,不过归并排序也分为两种,一种是通过递归自顶向下归并,另一种是自底向上的归并。对于前者空间复杂度取决于递归栈,而后者的空间复杂度为O(1),所以该题我们选择用自底向上的归并排序解题。
思路其实很简单,先统计链表的总长度,然后先根据长度为1划分链表,按顺序合并两个划分部分,合并后长度为2且有序。当所有划分部分合并后,根据长度2划分链表……重复以上步骤,每次划分长度都翻倍。
这样当划分长度大于等于链表长度时就说明我们已经排序好了整个链表,可以返回结果了。
对于如何合并两个有序链表,可以参考我之前写的“21.合并两个有序链表”题解。
LeetCode0021.合并两个有序链表 Go语言AC笔记https://blog.csdn.net/Hexa_H/article/details/127278986
AC代码
整体参考的官方题解代码,并进行了小小的优化,时间效率高一些。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
res:=&ListNode{Next:head}
length:=0
for head!=nil{
length++
head=head.Next
}
for l:=1;l<length;l<<=1{
pre,cur:=res,res.Next
for cur!=nil{
head1:=cur
for i:=1;i<l&&cur.Next!=nil;i++{
cur=cur.Next
}
head2:=cur.Next
if head2==nil{
pre.Next=head1
break
}
cur.Next=nil
cur=head2
for i:=1;i<l&&cur.Next!=nil;i++{
cur=cur.Next
}
next:=cur.Next
cur.Next=nil
pre.Next=merge(head1,head2)
if next==nil{
break
}
for pre.Next!=nil{
pre=pre.Next
}
cur=next
}
}
return res.Next
}
func merge(a,b *ListNode)*ListNode{
res:=&ListNode{-1,nil}
p:=res
for a!=nil&&b!=nil{
if a.Val<b.Val{
p.Next=a
a=a.Next
}else{
p.Next=b
b=b.Next
}
p=p.Next
}
if a==nil{
p.Next=b
}
if b==nil{
p.Next=a
}
return res.Next
}
感悟
链表里最常用的排序算法就是归并排序,其中自底向上的归并排序空间复杂度为O(1),务必要掌握。