题目描述
给你链表的头结点 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 = []
输出:[]
归并排序的简要步骤:将数组划分为只有一个元素的子数组,然后依次向上合并,直到合并到数组长度等于原始数组长度。类似于下图:
对于链表而言求解时也是按照这种思路,只不过多了一些链表的属性。首先统计链表的长度,由于最后链表的头结点会发生变化,因此设置一个虚拟头结点。
- 将数组划分,划分长度为1,2,4……
- 合并每一层的链表,合并时两两合并,需要找到合并时的第一个链表的头结点以及第二个链表的头结点,然后按照归并的思路合并,合并结束后会出现一些未被合并的部分,需要再一次合并。合并结束后该层的下一个需要合并的两个链表再依次进行上述的操作。cur的next指针需要指向second,即将这层的链表连起来,此时second是本层这一次合并的第一个链表的头结点。
class Solution {
public:
ListNode* sortList(ListNode* head) {
int n=0;
for(auto p=head;p;p=p->next) n++;//统计链表节点个数
auto dummpy=new ListNode(-1);//虚拟头结点
dummpy->next=head;
for(int i=1;i<n;i*=2)//分层
{
auto cur=dummpy;
for(int j=0;j+i<n;j+=2*i)//对每层操作,每层需要合并的链表为2*i个,需要合并 i 组
{
auto first=cur->next,second=first;//该层需要合并的两个链表
for(int k=0;k<i;k++) second=second->next;
int f=0,s=0;
while(f<i && s<i && second)
{
if(first->val <= second->val) cur=cur->next=first,first=first->next,f++;
else cur=cur->next=second,second=second->next,s++;
}
//有可能有部分没合并
while(f<i) cur=cur->next=first,first=first->next,f++;//第一部分长度肯定是i
while(s<i&&second) cur=cur->next=second,second=second->next,s++;//第二部分长度有可能小于i
cur->next=second;//将排好序的链表尾 链接 到 j+2i 下一2i链表 的表头,循环完毕后 second 为下一2i链表表头
}
}
return dummpy->next;
}
};