思路:
归并排序是链表排序的最佳方案,时间复杂度是 O(nlogn) 的排序算法包括归并排序、堆排序和快速排序(快速排序的最差时间复杂度是 O(n^2)
代码:
class Solution {
public ListNode sortList(ListNode head) {
if(head==null){
return head;
}
int length=0;
ListNode node=head;
while(node!=null){
//遍历链表,记录长度
length++;
node=node.next;
}
ListNode hair=new ListNode(0);
hair.next=head;
for(int len=1;len<length;len*=2){
ListNode pre=hair;
ListNode curr=hair.next;
//循环开始
while(curr!=null){
ListNode head1=curr;
for(int i=1;i<len&&curr!=null&&curr.next!=null;i++){
curr=curr.next;
}
ListNode head2=curr.next;
//第一次切断
curr.next=null;
curr=head2;
for(int i=1;i<len&&curr!=null&&curr.next!=null;i++){
curr=curr.next;
}
ListNode next=null;
if(curr!=null){
next=curr.next;
//第二次切断
curr.next=null;
}
//合并2个有序链表
ListNode merged=merge(head1,head2);
pre.next=merged;
while(pre.next!=null){
pre=pre.next;
}
curr=next;
}
}
//返回新排好序的链表
return hair.next;
}
private ListNode merge(ListNode l1,ListNode l2){
ListNode hair=new ListNode(0);
ListNode curr=hair;
while(l1!=null&&l2!=null){
if(l1.val<l2.val){
curr.next=l1;
l1=l1.next;
}
else{
curr.next=l2;
l2=l2.next;
}
curr=curr.next;
}
if(l1==null) curr.next=l2;
if(l2==null) curr.next=l1;
return hair.next;
}
}
分解:
1)归并排序的本质是分治+合并,但自顶向下要开辟栈,不符合题目要求的常数级空间复杂度
本题的解法是归并排序的自底向上方法
2)经过2次切割,合并2个链表
3)要熟悉合并2个排好序的链表的固定代码
复杂度分析:
时间复杂度:O(NlogN)归并排序的时间复杂度为NlogN
空间复杂度:O(1)只有指针,都是常数级的额外空间