写单链表的时候突然想到,链表该怎么排序呢? 毕竟我们链表也要有自己的O(nlogn)排序方法!
第一个想法:用quicksort,带头尾指针的双链表确实可以,但是效率不优,因为在选择pivot的时候只能选择最左端,很容易退化成冒泡,更别说单链表了压根就不能用了。
第二个想法:嗯嗯嗯....这种问题肯定前人已经解决过了。所以去看看stl库吧。
用C++ list库,然后写一行 .sort() 再看定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 template <typename _Tp, typename _Alloc> void list<_Tp, _Alloc>:: sort () { 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数组,充分发挥了链表的优势,如果数组用这种方法反而会变慢。
我这里只是用单链表进行了实践,双向链表也是一样的。
首先定义:
1 2 3 4 typedef struct LIST { int data; LIST*next; }*Linklist;
几个辅助函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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():
1 2 3 4 bool empty (Linklist&p) { return p==NULL ||p->next==NULL ; }
splice(): 因为只需要用到第一个节点,所以我这里只要了首节点。
1 2 3 4 5 6 7 void splice_front (Linklist&head,Linklist & p) { Linklist temp=head->next; head->next=temp->next; temp->next=p->next; p->next=temp; }
merge():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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部分的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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 ; }
全部代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 #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设计的。