Sort a linked list in O(n log n) time using constant space complexity.
算法一:快速排序。 因为链表是单向,所以有很多细节要注意,比如quick_sort(p,r)排序的区间其实是[p->next,r->next],因为单向链表不能回头。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//p,r分别为欲排序的子链表的首尾节点的前驱
void quick_sort(ListNode *p,ListNode *r){
if(p==r ||r == NULL || p==r->next) return;
ListNode *k = p,*pk = NULL; //pk为k的前驱
ListNode *q,*tail = r->next;
//注意r在迭代过程中可能会被改变,因此控制条件不能为q!=r,tail是迭代过程中是不会被改变的
for(q=p;q->next!=tail;){
if(q->next->val <= tail->val){
bool flag = (q==k->next);
Swap(k,q); //交换k->next与q->next节点
pk = k; k = k->next;
if(!flag) q = q->next; //若k,q相邻则swap后q会后移一位,此时无需令q=q->next,注意k,q都是欲交换节点的前驱,非节点本身
}
else q = q->next;
}
bool flag = (k->next==q || k==q);
Swap(k,q);
quick_sort(p,pk);
if(!flag) quick_sort(k->next,q);
}
//当交换相邻的节点时要特别考虑
void Swap(ListNode *p, ListNode *r){
if(p==r) return;
else if(p==r->next) Swap(r,p);
else if(r==p->next){
ListNode *ssr = r->next->next;
p->next = r->next;
r->next->next = r;
r->next = ssr;
}
else{
ListNode *sp = p->next, *ssp = sp->next, *ssr = r->next->next;
p->next = r->next; r->next->next = ssp;
r->next = sp; sp->next = ssr;
}
}
ListNode *sortList(ListNode *head) {
if(head==NULL || head->next==NULL) return head;
ListNode* tmp_head = new ListNode(0);
tmp_head->next = head; //构造头节点的前驱
ListNode* ptail = tmp_head; //ptail为尾节点的前驱
while(ptail->next->next) ptail = ptail->next;
quick_sort(tmp_head,ptail);
ListNode* new_head = tmp_head->next;
delete tmp_head;
return new_head;
}
};
算法二: 改进版快速排序。 快速排序在数据随机分布情况下速度较快,但在本题中会超时。查看超时的test case,发现有大量重复值。于是想到改进的算法,抛弃传统的两段划分,采用三段划分。{x:x < tail->val}, {y:y == tail->val}, {z:z > tail->val}. 每次分割后对中间段不再调用quick_sort。改进后可以AC
改进后的快排算法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//p,r分别为欲排序的子链表的首尾节点的前驱
//每轮递归后都保持格局{x:x->val < tail->val},{y:y->val == tail->val}, {z:z->val > tail->val}
void quick_sort(ListNode *p,ListNode *r){
if(p==r ||r == NULL || p==r->next) return;
//k1指向等于tail->val的连续节点段首的前驱,若无此连续段,则k1与q同
//k2指向大于tail->val的连续节点段首的前驱,若无此连续段,则k2与q同
ListNode *k1 = p, *k2 = p, *pk1 = NULL, *pk2 = NULL; //pk为k的前驱
ListNode *q,*tail = r->next,*pq = NULL;
//注意r在迭代过程中可能会被改变,因此控制条件不能为q!=r,tail是迭代过程中是不会被改变的
for(q=p;q->next!=tail;){
if(q->next->val < tail->val){
bool flag1 = (q==k1->next);
if(!flag1 && k1->next == k2) k2 = q->next; //若k2是k1的后继,且大于tail->val的节点真实存在
Swap(k1,q); //交换k1->next与q->next节点,将小于tail->val的节点移到等于tail->val的节点的前面,或保持不变(交换相同节点)
pk1 = k1; k1 = k1->next;
if(pk1==k2 || k2==q){ //大于、等于tail->val的节点只现其一
if(k2!=q){ //即k1==k2,只出现大于tail->val的节点,没出现过等于tail->val的
k2 = k1;
if(!flag1) q = q->next; //若flag1==true说明Swap时q已经向后移了一位,不用再右移
}
else{ //k1<k2的情况,只出现过等于tail->val的节点,没出现过大于tail->val的
if(!flag1){ q = q->next; k2 = q;}
}
}
//若在前面同时出现了大于、等于tail->val的两种节点,则还要再将等于tail->val的节点与大于tail->val的节点交换
else{
bool flag2 = (q==k2->next);
Swap(k2,q);
k2 = k2->next;
if(!flag2) q = q->next;
}
}
else if(q->next->val == tail->val){ //将等于tail->val的节点移到大于tail->val的节点的前面
bool flag2 = (q==k2->next);
Swap(k2,q);
k2 = k2->next;
if(!flag2) q = q->next;
}
else q = q->next;
}
bool flag = (k2->next==q || k2==q);
Swap(k2,q);
quick_sort(p,pk1);
if(!flag) quick_sort(k2->next,q);
}
//当交换相邻的节点时要特别考虑
void Swap(ListNode *p, ListNode *r){
if(p==r) return;
else if(p==r->next) Swap(r,p);
else if(r==p->next){
ListNode *ssr = r->next->next;
p->next = r->next;
r->next->next = r;
r->next = ssr;
}
else{
ListNode *sp = p->next, *ssp = sp->next, *ssr = r->next->next;
p->next = r->next; r->next->next = ssp;
r->next = sp; sp->next = ssr;
}
}
ListNode *sortList(ListNode *head) {
if(head==NULL || head->next==NULL) return head;
ListNode* tmp_head = new ListNode(0);
tmp_head->next = head; //构造头节点的前驱
ListNode* ptail = tmp_head; //ptail为尾节点的前驱
while(ptail->next->next) ptail = ptail->next;
quick_sort(tmp_head,ptail);
ListNode* new_head = tmp_head->next;
delete tmp_head;
return new_head;
}
};
算法三:递归版归并排序。 除了快速排序,当然也可以用归并排序。链表的归并排序与数组的归并排序唯一不同是数组可以在O(1)时间取到中点,但链表要花费O(n),有人因此就认为归并排序不适合链表了,其实不然。即使找中点要花费O(n)又怎样呢?事实是不管是数组还是链表,都要有个合并过程,而合并的时间复杂度就是O(n),因此取中点的复杂度是O(1)还是O(n)都不会影响到整个排序的时间复杂度,链表和数组的归并排序时间复杂度上仅在于系数的差别而已。都是O(n*log n)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
typedef pair<ListNode*,ListNode*> pair_node;
pair_node merge_sort(ListNode* first,ListNode* last){
if(first==last || first->next==last) return make_pair(first,last);
ListNode *fast = first, *slow = first;
while(fast!=last && fast->next!=last){
fast = fast->next->next;
slow = slow->next;
}
pair_node list1 = merge_sort(first,slow);
pair_node list2 = merge_sort(slow,last);
return merge(list1,list2);
}
pair_node merge(pair_node list1,pair_node list2){
ListNode *first;
ListNode *cur,*p = list1.first, *q = list2.first;
if(p->val <= q->val){
cur = first = p;
p = p->next;
}
else{
cur = first = q;
q = q->next;
}
while(p!=list1.second && q!=list2.second){
if(p->val <= q->val){
cur->next = p; cur = p;
p = p->next;
}
else{
cur->next = q; cur = q;
q = q->next;
}
}
if(p!=list1.second){
cur->next = p;
while(p->next!=list1.second) p = p->next;
p->next = list2.second;
}
else cur->next = q;
return make_pair(first,list2.second);
}
ListNode *sortList(ListNode *head) {
if(head==NULL || head->next==NULL) return head;
return merge_sort(head,NULL).first;
}
};
因为用归并排序并不像快排在找分割点的过程中需要交换节点,所以在处理上要比快排容易,只需用一个首节点和尾节点的后继表示链表就可以了。
算法四:非递归归并排序。上面的归并排序是采用传统的递归实现。归并排序也可以用迭代实现,实质是递归的逆向实现。其思路是这样,假设有n个节点,从第一个节点1开始,发现此前没有已经排好的长度为1的链表(即单个节点),到2时发现此前有单节点1,则把(1,2)合并成长度为2的已排序链表,再看此前有没有长度为2的已排序的子链表,没有。再取3,发现此前无单节点(1,2已经合并),再取4,发现有单节点3,合并(3,4),合并后长度为2,再查找此前有没有长度为2的已排序子链表,刚好有!于是将子链表(1,2)与子链表(3,4)合并,合并后长度为4......
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge(ListNode *list1,int len1,ListNode *list2,int len2){
if(len1==0) return list2;
if(len2==0) return list1;
int i=0,j=0;
ListNode *res,*cur,*p=list1,*q=list2;
if(p->val <= q->val){ res = cur = p; p = p->next; i++; }
else{ res = cur = q; q = q->next; j++; }
while(i<len1 && j<len2){
if(p->val <= q->val){
cur->next = p; cur = p;
i++; p = p->next;
}
else{
cur->next = q; cur = q;
j++; q = q->next;
}
}
if(i<len1){
cur->next = p;
while(++i<len1) p = p->next;
p->next = q;
}
else cur->next = q;
return res;
}
ListNode *sortList(ListNode *head) {
if(head==NULL || head->next==NULL) return head;
ListNode* tab[64]; //tab[i]表示从tab[i]开始的连续2^i个节点已经排好序,各tab[i]之间没有交集
for(int i=0;i<64;i++) tab[i] = NULL;
int k = 0; //k表示tab[k-1]是最后一个非空的节点指针
ListNode *cur = head;
ListNode *carry;
while(cur!=NULL){
carry = cur;
cur = cur->next;
for(int i=0;i<=k;i++){
if(tab[i]==NULL){ //第一次出现的为NULL的tab[i],则将其指向为之前合并的链表的首节点
tab[i] = carry;
if(i==k) k++;
break;
}
else{
int len = 1<<i;
carry = merge(tab[i],len,carry,len); //合并长度相同的已排序链表
tab[i] = NULL; //清空tab[i]
}
}
}
carry = NULL;
int curLen = 0;
//合并tab中的零散子链表
for(int i=0;i<k;i++){
if(tab[i]!=NULL){
carry = merge(tab[i],1<<i,carry,curLen);
tab[i] = NULL;
curLen += (1<<i);
}
}
return carry;
}
};