最近想鑽研一下STL原始碼,於是照著侯捷的《STL源碼剖析》看SGI STL,今天想寫寫list的排序演算法。原始碼如下:
template <class _Tp, class _Alloc> template <class _StrictWeakOrdering>
void list<_Tp, _Alloc>::sort(_StrictWeakOrdering __comp)
{
// Do nothing if the list has length 0 or 1.
if (_M_node->_M_next != _M_node && _M_node->_M_next->_M_next != _M_node) {
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, __comp);
__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], __comp);
swap(__counter[__fill-1]);
}
}
侯捷說這是快排演算法!不可思議,雖然第一眼看上去看不出什麼,但第一印象絕對不是快排,多半是合并排序。經過自己的深入研究,現在終於可以打包票了!
首先,要明白 __counter[64] 的含義。__counter[i] 表示長度為 2^i 的一段有序資料,當然是來自於待排序的list。fill 表示現在最大的有序資料長度——2^fill。
每次 carry 從list中擷取一個資料,和 __counter[0] 合并,並把__counter[0] 的內容給carry,且清空__counter[0]。接著carry和__counter[1] 合并,把__counter[1]賦給carry,清空__counter[1], 以此類推,一直到 __counter[i] 為空白,或是 i==__fill(此時carry的長度是 2^(__fill+1) )。接著我們可以看到 __fill ++ 。
我想要注意一下__counter[i] 的長度為空白時的情況。此時 carry 的有序資料的個數是 2^i ,但是長度是 2^i 的緩衝區__counter 為空白,毫無疑問,我們應該用__counter[i] 儲存 carry 的有序資料。
最後,當list為空白時,資料散落在 __counter[] 中,它們都是有序的,只需要以此合并就可以了。時間複雜度就不用說了。
感覺思路真的很巧妙,自己有必要實現一個簡單的list,且重點實現排序演算法,算是練練手吧,好久沒敲過代碼。
睡覺!