写单链表的时候突然想到,链表该怎么排序呢? 毕竟我们链表也要有自己的O(nlogn)排序方法!
第一个想法:用quicksort,带头尾指针的双链表确实可以,但是效率不优,因为在选择pivot的时候只能选择最左端,很容易退化成冒泡,更别说单链表了压根就不能用了。
第二个想法:嗯嗯嗯....这种问题肯定前人已经解决过了。所以去看看stl库吧。
用C++ list库,然后写一行 .sort() 再看定义。
template<typename _Tp, typename _Alloc>
void
list<_Tp, _Alloc>::
sort()
{
// Do nothing if the list has length 0 or 1.
if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node
&& this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node)
{
list __carry;
list __tmp[64];
list * __fill = __tmp;
list * __counter;
__try
{
do
{
__carry.splice(__carry.begin(), *this, begin());
for(__counter = __tmp;
__counter != __fill && !__counter->empty();
++__counter)
{
__counter->merge(__carry);
__carry.swap(*__counter);
}
__carry.swap(*__counter);
if (__counter == __fill)
++__fill;
}
while ( !empty() );
for (__counter = __tmp + 1; __counter != __fill; ++__counter)
__counter->merge(*(__counter - 1));
swap( *(__fill - 1) );
}
__catch(...)
{
this->splice(this->end(), __carry);
for (int __i = 0; __i < sizeof(__tmp)/sizeof(__tmp[0]); ++__i)
this->splice(this->end(), __tmp[__i]);
__throw_exception_again;
}
}
}
这里确实有点看不太懂。
关于stl这里的源码解析,推荐:C++ SGI STL 的 list::sort() 分析_list.sort函数 c++-CSDN博客
解析的很清晰,用的是非递归的mergesort,尤其是运用splice函数和counter数组,充分发挥了链表的优势,如果数组用这种方法反而会变慢。
我这里只是用单链表进行了实践,双向链表也是一样的。
首先定义:
typedef struct LIST{
int data;
LIST*next;
}*Linklist;
几个辅助函数:
void init_list(Linklist & list)
{
Linklist p= new LIST;
list=p;
list->next=NULL;
}
//尾插法初始化
void init(Linklist&list)
{
int n,x;
Linklist p=list;
cin>>n;
while(n--)
{
cin>>x;
Linklist temp=new LIST;
temp->next=NULL;
temp->data=x;
p->next=temp;
p=p->next;
}
}
void show(Linklist&list)
{
Linklist p=list;
while(p->next!=NULL)
{
cout<<p->next->data<<' ';
p=p->next;
}
cout<<endl;
return;
}
然后是stl库中几个辅助函数的时间:
empty():
bool empty(Linklist&p)
{
return p==NULL||p->next==NULL;
}
splice(): 因为只需要用到第一个节点,所以我这里只要了首节点。
void splice_front(Linklist&head,Linklist & p)
{
Linklist temp=head->next;
head->next=temp->next;
temp->next=p->next;
p->next=temp;
}
merge():
void list_merge(Linklist &list,Linklist& p)
{
Linklist it=list,p1=list->next,p2=p->next;
while(p1&&p2)
{
if(p1->data<p2->data)
{
it->next=p1;
p1=p1->next;
}
else
{
it->next=p2;
p2=p2->next;
}
it=it->next;
}
if(!p1)it->next=p2;
else it->next=p1;
p->next=NULL;
}
最后是sort部分的函数:
void list_sort(Linklist& list)
{
if(list->next==NULL||list->next->next==NULL)return;
Linklist carry=new LIST;
Linklist counter[64];
for(int i=0;i<64;++i)
counter[i]=new LIST,counter[i]->next=NULL;
carry->next=NULL;
int fill=0;
while(!empty(list))
{
splice_front(list,carry);
int i=0;
while(i<fill&&!empty(counter[i]))
{
list_merge(counter[i],carry);
swap(carry,counter[i++]);
}
swap(carry,counter[i]);
if(i==fill)++fill;
}
for(int i=1;i<fill;++i)
list_merge(counter[i],counter[i-1]);
list->next=counter[fill-1]->next;
for(int i=0;i<64;++i)
delete counter[i];
delete carry;
return;
}
全部代码:
#include <iostream>
#include <list>
#define ll long long
using namespace std;
typedef struct LIST{
int data;
LIST*next;
}*Linklist;
void init_list(Linklist & list)
{
Linklist p= new LIST;
list=p;
list->next=NULL;
}
//尾插法初始化
void init(Linklist&list)
{
int n,x;
Linklist p=list;
cin>>n;
while(n--)
{
cin>>x;
Linklist temp=new LIST;
temp->next=NULL;
temp->data=x;
p->next=temp;
p=p->next;
}
}
void show(Linklist&list)
{
Linklist p=list;
while(p->next!=NULL)
{
cout<<p->next->data<<' ';
p=p->next;
}
cout<<endl;
return;
}
bool empty(Linklist&p)
{
return p==NULL||p->next==NULL;
}
void splice_front(Linklist&head,Linklist & p)
{
Linklist temp=head->next;
head->next=temp->next;
temp->next=p->next;
p->next=temp;
}
void list_merge(Linklist &list,Linklist& p)
{
Linklist it=list,p1=list->next,p2=p->next;
while(p1&&p2)
{
if(p1->data<p2->data)
{
it->next=p1;
p1=p1->next;
}
else
{
it->next=p2;
p2=p2->next;
}
it=it->next;
}
if(!p1)it->next=p2;
else it->next=p1;
p->next=NULL;
}
void list_sort(Linklist& list)
{
if(list->next==NULL||list->next->next==NULL)return;
Linklist carry=new LIST;
Linklist counter[64];
for(int i=0;i<64;++i)
counter[i]=new LIST,counter[i]->next=NULL;
carry->next=NULL;
int fill=0;
while(!empty(list))
{
splice_front(list,carry);
int i=0;
while(i<fill&&!empty(counter[i]))
{
list_merge(counter[i],carry);
swap(carry,counter[i++]);
}
swap(carry,counter[i]);
if(i==fill)++fill;
}
for(int i=1;i<fill;++i)
list_merge(counter[i],counter[i-1]);
list->next=counter[fill-1]->next;
for(int i=0;i<64;++i)
delete counter[i];
delete carry;
return;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
Linklist it;
init_list(it);
init(it);
list_sort(it);
show(it);
cout<<endl;
system("pause");
return 0;
}
去洛谷试试吧:P1177 【模板】排序 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
用时104ms
内存3.67MB
内存逼近O(logn)但是list的空间密度较低,所以内存较大,时间和数组的quicksort逼近了。让我怀疑在数组中 nlogn 的排序方法中mergesort之所以是 n 的空间复杂度,就是因为它实际上是为list设计的。