https://leetcode-cn.com/problems/sort-list/
思路一:由于链表这种数据结构的特殊性,想要在
O
(
n
l
g
n
)
O(nlgn)
O(nlgn)时间复杂度下完成排序,就只能选择归并排序啦。递归版的很简单,利用快慢指针计算出链表中点,将链表一分为二然后递归下去即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//得到链表的中间节点
ListNode* getMid(ListNode* head){
//这一步是为了使得 当链表有偶数个元素时 返回的是第一个中间节点
ListNode root(0);
root.next=head;
ListNode *slow=&root,*fast=&root;
while(fast){
fast=fast->next;
if(fast){
fast=fast->next;
slow=slow->next;
}
}
return slow;
}
ListNode* merge(ListNode* l,ListNode *r){
ListNode head(0);
ListNode *cur=&head;
while(l&&r){
if(l->val<=r->val)
cur->next=l,l=l->next;
else
cur->next=r,r=r->next;
cur=cur->next;
}
cur->next=l?l:r;
return head.next;
}
ListNode* mergeSort(ListNode* head){
if(!head->next)
return head;
ListNode *r=getMid(head);
ListNode *tmp=r->next;
//断开连接
r->next=nullptr;
ListNode *l=mergeSort(head);
r=mergeSort(tmp);
return merge(l,r);
}
ListNode* sortList(ListNode* head) {
if(!head)
return head;
return mergeSort(head);
}
};
思路二:现在来看看非递归版怎么写。假设我们需要进行
x
x
x轮循环,第一轮循环可以依次合并
2
2
2个元素,第二轮循环可以依次合并
4
4
4个元素(如下图所示)……
那么很容易知道
x
=
l
o
g
2
n
x=log_2n
x=log2n,所以总复杂度依然是
O
(
n
l
g
n
)
O(nlgn)
O(nlgn)。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//将链表head一分为二 且第一部分有n个元素 返回第二部分的头节点
ListNode* cut(ListNode* head,int n){
while(--n&&head){
head=head->next;
}
if(!head)
return head;
ListNode *nxt=head->next;
head->next=nullptr;
return nxt;
}
//合并两个有序链表
ListNode* merge(ListNode* l,ListNode* r){
ListNode head(0);
ListNode *cur=&head;
while(l&&r){
if(l->val<=r->val)
cur->next=l,l=l->next;
else
cur->next=r,r=r->next;
cur=cur->next;
}
cur->next=l?l:r;
return head.next;
}
ListNode* sortList(ListNode* head) {
if(!head)
return head;
int len=0;
ListNode *l=head,*r,*tmp,*res;
while(l&&++len)
l=l->next;
ListNode root(0);
root.next=head;
//每次合并2*siz个元素
for(int siz=1;siz<len;siz<<=1){
//从头开始
res=&root;
tmp=root.next;
while(tmp){
//l是有序链表A r是有序链表B
l=tmp;
r=cut(l,siz);
//注意要断开r与后者的连接(保证r的元素个数<=siz)
tmp=cut(r,siz);
//合并l r 并将其串接到前一次结果之后
res->next=merge(l,r);
//为了保证每次都正确的串接上了 需要遍历到res的最后一个节点
while(res->next)
res=res->next;
}
}
return root.next;
}
};
思路三:整一个骚操作。我们用 v e c t o r vector vector存储链表的节点,那么很容易实现出一个自定义比较函数,这样就可以愉快的使用 s o r t sort sort辣。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
static bool cmp(ListNode* l,ListNode *r){
return l->val<r->val;
}
ListNode* sortList(ListNode* head) {
if(!head)
return head;
vector<ListNode*> vec;
while(head){
vec.push_back(head);
head=head->next;
}
sort(vec.begin(),vec.end(),cmp);
int siz=vec.size();
--siz;
for(int i=0;i<siz;i++)
vec[i]->next=vec[i+1];
vec[siz]->next=nullptr;
return vec[0];
}
};