最近想钻研一下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,且重点实现排序算法,算是练练手吧,好久没敲过代码。
睡觉!