给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3] 输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0] 输出:[-1,0,3,4,5]
示例 3:
输入:head = [] 输出:[]
提示:
[0, 5 * 104]
内-105 <= Node.val <= 105
进阶:你可以在 O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
链表排序,还是有一定难度的,这里有这样一种思路:先把链表遍历一遍,得到元素个数,然后用malloc申请对应个数的空间,将链表元素全部存入申请出来的数组中,在用一些排序方式(快排、希尔、堆排 等)对数组进行排序,然后把排好序的数据在写回到链表中。
在这里我不使用以上的思路,而是要直接对链表进行操作,从而达到链表排好序的目的。其中递归和非递归的代码思路都是归并排序。
1、找到链表的中点,以中点为分界,将链表拆分成两个子链表。
2、对两个子链表分别排序。
3、将两个排序后的子链表合并,得到完整的排序后的链表。
上述过程可以通过递归实现。递归的终止条件是链表的节点个数小于或等于 1,即当链表为空或者链表只包含 1个节点时,不需要对链表进行拆分和排序。
寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。
合并两个链表可以用尾插的方法,将节点不断尾插到给定的表头之后。
struct ListNode* Mergedata(struct ListNode* head1,struct ListNode* head2){//合并两个有序链表
struct ListNode s;//给定链表头
struct ListNode* cur=&s;//头指针,指向链表头
while(head1&&head2){//将两个有序链表进行尾插到s之后
if(head1->val<=head2->val){
cur->next=head1;
head1=head1->next;
}else{
cur->next=head2;
head2=head2->next;
}
cur=cur->next;
}
if(head1){//尾插剩余部分
cur->next=head1;
}else{
cur->next=head2;
}
return s.next;
}
struct ListNode* Middle(struct ListNode* head){
//寻找链表的中间节点,并返回地址,注意此函数将把一个链表一分为二。
struct ListNode* prev=NULL;
struct ListNode* slow=head;
struct ListNode* fast=head;
while(fast&&fast->next){
prev=slow;
slow=slow->next;
fast=fast->next->next;
}
prev->next=NULL;
return slow;
}
struct ListNode* sortList(struct ListNode* head){
if(head==NULL||head->next==NULL){
return head;
}
struct ListNode* cur=Middle(head);//拿到中间节点地址
head=sortList(head);//将前半部分排好序
cur=sortList(cur);//将后半部分排好序
return Mergedata(head,cur);//归并前后两部分
}
看完以上代码,使用递归的话,就会发现这道题其实就是:合并两个有序链表、找到链表的中间节点、归并排序 这三道题的融合。
时间复杂度:归并排序,时间复杂度为O(NlogN)。
空间复杂度:递归深度为logN,所以空间复杂度为O(logN)。
因为题目进阶要求空间复杂度为O(1),很显然以上递归代码不满足要求。那么此时就需要想办法将递归转循环了。
首先求得链表的长度 len,然后将链表拆分成子链表进行合并。
具体做法如下:
1、用step表示每次需要排序的子链表的长度,初始时step=1;
2、每次将链表拆分成若干个长度为step的子链表(最后一个子链表的长度可以小于 step),按照每两个子链表一组进行合并,合并后即可得到若干个长度为2*step的有序子链表(最后一个子链表的长度可以小于2*step)。
3、将 step的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 len,整个链表排序完毕。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* Mergedata(struct ListNode* head1,struct ListNode* head2){//合并两个有序链表
struct ListNode s;
struct ListNode* cur=&s;
while(head1&&head2){
if(head1->val<=head2->val){
cur->next=head1;
head1=head1->next;
}else{
cur->next=head2;
head2=head2->next;
}
cur=cur->next;
}
if(head1){
cur->next=head1;
}else{
cur->next=head2;
}
return s.next;
}
struct ListNode* Disconnect(struct ListNode* head,int step){
//将链表按照step长度的拆分,并返回拆分后剩余部分的地址
if(head==NULL){
return NULL;
}
struct ListNode* cur=head;
for(int i=0;head&&inext;
}
cur->next=NULL;
return head;
}
int Length(struct ListNode* head){//求链表长度
int count=0;
while(head){
head=head->next;
count++;
}
return count;
}
struct ListNode* sortList(struct ListNode* head){
struct ListNode s;//给定头节点,方便尾插
struct ListNode* cur=&s;//指向给定的头节点
cur->next=head;//先连接给定的链表,这一步必须做
int len=Length(head);
for(int step=1;stepnext=Mergedata(h1,h2);//合并第一与第二部分,并尾插在s之后
while(cur->next){//cur往前走,便于下次尾插
cur=cur->next;
}
}
}
return s.next;
}
时间复杂度:归并排序,时间复杂度为O(NlogN)。
空间复杂度:没有申请额外空间,空间复杂度为O(1)。