首先看到题目中要求时间复杂度 O(n log n),时间复杂度为常数的时候,先将最容易实现的插入排序排除(O(n^2));接着想到快速排序和归并排序,
但是快速排序的最坏时间复杂度也为O(n^2),想了一会并且感觉也不是很好实现的样子。最后瞅了一眼discuss的代码,发现用的是归并排序,理论上归并排序的空间复杂度是线性的,也就是O(N),并不是常数,这点就需要结合链表的特性,只需要修改指针之间的关系就可以了,并不需要额外的空间开销,只需要new一个新的链表头即可。
由此分析,采用结合链表特性的归并排序是最好的方法。
我们使用归并排序时,先递归分解,每次分成两半,这里就需要用到追及法,也就是一个指针走2步,一个走一步,快的到末尾时,慢的正好到中间。
然后再使用merge函数由底至上归并为有序链表。具体看代码吧,注释很详细。
//归并排序(链表)
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head==NULL||head->next==NULL) //处理0个或1个的情况
return head;
return mergeSort(head); //因为要递归本身,所以另写一个函数
}
ListNode* mergeSort(ListNode* head) //递归分解
{
if(head==NULL||head->next==NULL)
return head;
//追及法求中点
ListNode* p=head;
ListNode* q=head;
ListNode* p_pre=NULL;
while(q!=NULL&&q->next!=NULL)
{
q=q->next->next;
p_pre=p; //保存前半段的最后一个元素
p=p->next;
}
p_pre->next=NULL;
ListNode* left=mergeSort(head); //递归前半段
ListNode* right=mergeSort(p); //递归后半段
return merge_top(left,right);
}
ListNode* merge_top(ListNode* left,ListNode*right) //对有序的进行归并
{
ListNode* temp=new ListNode(0);
ListNode* cur=temp;
while(left!=NULL&&right!=NULL)
{
if(left->val<=right->val)
{
cur->next=left;//只需修改指针指向,无需开辟新的空间
left=left->next;
}
else
{
cur->next=right;
right=right->next;
}
cur=cur->next;
}
if(left!=NULL)
cur->next=left;
else if(right!=NULL)
cur->next=right;
else
cur->next=NULL;
cur=temp->next; //返回第一个有内容的链表头部
delete temp;
return cur;
}
};