STL中容器list的sort方法詳解
**
List 不能使用STL提供的演算法 sort() , 必須使用自己定義的sort() member function,因為STL演算法sort()只接受RamdonAccessIterator,它的實現程式碼如下,是一個quick sort;
**
list::sort的程式碼如下(sgi stl):
template <class _Tp, class _Alloc>
void list<_Tp, _Alloc>::sort()
{
// Do nothing if the list has length 0 or 1.
//以下判斷如果是空連結串列,或者是隻有一個元素,就不進行任何操作
//使用四則size()== 0 || size()== 1來判斷,雖然可以但緩慢些
if (_M_node->_M_next != _M_node && _M_node->_M_next->_M_next != _M_node) {
//一些新的list作為中介資料儲存區
list<_Tp, _Alloc> __carry;
list<_Tp, _Alloc> __counter[64];
int __fill = 0;
while (!empty()) {
__carry.splice(__carry.begin(), *this , begin());
int __i = 0;
while(__i < __fill && !__counter[__i].empty()) {
__counter[__i].merge(__carry);
__carry.swap(__counter[__i++]);
}
__carry.swap(__counter[__i]);
if (__i == __fill) ++__fill;
}
for (int __i = 1; __i < __fill; ++__i)
__counter[__i].merge(__counter[__i-1]);
swap(__counter[__fill-1]);
}
}
雖然短短几行,但真心看了好久,挺撲朔迷離的!下面直接給出個例項,相信簡單很多。
比如我們的list裡有如下幾個需要排序的元素:21,45,1,30,52,3,58,47,22,59,0,58。
排序的時候怎麼做,我們先定義若干中轉list在上述程式碼中定義了64個元素的陣列
list<_Tp, _Alloc> __counter[64]; 其中裡邊存什麼呢?他們都是用來中轉用的
__counter[0]裡存放2(0+1)次方個元素
__counter[1]裡存放2(1+1)次方個元素
__counter[2]裡存放2(2+1)次方個元素
__counter[3]裡存放2(3+1)次方個元素,依次類推
那又是怎麼個存放方法呢?一個指導原則就是當第i個元素即__counter[i]的內容個數等於2(i+1)次方時,就要把__counter[i]的資料轉移給__count[i+1]。
具體過程如下:
取出第1個數21,放到__counter[0]裡,這時__counter[0]裡有一個元素,小於2,繼續
__counter[0]: 21
__counter[1]: NULL
取出第2個數45,放到__counter[0]裡(不是簡單的放,而是排序放,類似兩個list做merge),這時__counter[0]裡有2個元素了,需要把這兩個元素轉移到__counter[1].
__counter[0]: NULL
__counter[1]: 21,45
取出第3個數1,放到__counter[0]裡,__count[0]與__count[1]都小於規定個數
__counter[0]: 1
__counter[1]: 21,45
取出第4個數30,放到__counter[0]裡,這時__counter[0]的個數等於2了,需要轉移到__counter[1]裡
__counter[0]: NULL
__counter[1]: 1,21,30,45
但這時__counter[1]裡的個數又等於4了,所有需要把__counter[1]的值轉移到__counter[2]裡,
__counter[0]: NULL
__counter[1]: NULL
__counter[2]: 1,21,30,45
然後取出52,放入__counter[0]
__counter[0]: 52
__counter[1]: NULL
__counter[2]: 1,21,30,45
然後取出3,放入__counter[0]
__counter[0]: 3,52
__counter[1]: NULL
__counter[2]: 1,21,30,45
這時候需要轉移
__counter[0]: NULL
__counter[1]: 3,52
__counter[2]: 1,21,30,45
然後取58
__counter[0]: 58
__counter[1]: 3,52
__counter[2]: 1,21,30,45
然後取47
__counter[0]: 47,58
__counter[1]: 3,52
__counter[2]: 1,21,30,45
需要轉移
__counter[0]: NULL
__counter[1]: 3,47,52,58
__counter[2]: 1,21,30,45
還需要轉移
__counter[0]: NULL
__counter[1]: NULL
__counter[2]: 1,3,21,30,47,45,52,58
還需要轉移
__counter[0]: NULL
__counter[1]: NULL
__counter[2]: NULL
__counter[3]: 1,3,21,30,47,45,52,58
然後再取59
__counter[0]: 59
__counter[1]: NULL
__counter[2]: NULL
__counter[3]: 1,3,21,30,47,45,52,58
然後取0
__counter[0]: 0,59
__counter[1]: NULL
__counter[2]: NULL
__counter[3]: 1,3,21,30,47,45,52,58
需要轉移
__counter[0]: NULL
__counter[1]: 0,59
__counter[2]: NULL
__counter[3]: 1,3,21,30,47,45,52,58
最後取58
__counter[0]: 58
__counter[1]: 0,59
__counter[2]: NULL
__counter[3]: 1,3,21,30,47,45,52,58
幾個相關的函式:
1.splice:把當前列表的__i位置元素刪除,儲存在__position裡
void list::splice(iterator __position, list&, iterator __i)
void list::splice(iterator __position, list&, iterator __i)
2.merge:把引數list的元素合併到當前list,引數list的內容會清空的
void list<_Tp, _Alloc>::merge(list<_Tp, _Alloc>& __x)
STL中的list被實現為環狀的雙向連結串列,設定一個“哨兵”node作為end( )。鑑於list的記憶體分配模型,list不能使用通用的標準sort演算法,而是實現自身的sort,但是list有自己的成員函式sort()可供其自身呼叫,其實際模型是基於合併排序的。普通的mergesort直接將待排序的序列一分為二,然後各自遞迴呼叫mergesort,再使用Merge演算法用O(n)的時間將已排完序的兩個子序列歸併,從而總時間效率為n*lg(n)。(mergesort是很好的排序演算法,絕對時間很小,n*lg(n)之前的係數也很小,但是在記憶體中的排序演算法中並不常見,我想可能主要還是因為耗空間太多,也是O(n)).
不過list_sort所使用的mergesort形式上大不一樣:將前兩個元素歸併,再將後兩個元素歸併,歸併這兩個小子序列成為4個元素的有序子序列;重複這一過程,得到8個元素的有序子序列,16個的,32個的。。。,直到全部處理完。主要呼叫了swap和merge函式,而這些又依賴於內部實現的transfer函式(其時間代價為O(1))。該mergesort演算法時間代價亦為n*lg(n),計算起來比較複雜。list_sort中預留了 64個temp_list,所以最多可以處理2^64-1個元素的序列,這應該足夠了。
類似2進位制,每一次進位都是相鄰高位數值的一半,所以是類2進位制地。例如8,低位4滿之後會進4個到8的。