STL中list链表的sort算法详解
今天在学习侯捷先生的《STL源码剖析》这本书,在讲到list链表中的sort算法时,书上写的不详细,很难在短时间内搞明白,我也花了好长时间才搞懂这个算法,下面我就来讲一讲这个算法的实现细节。
附代码如下:
template
void list
if(node->next==node || link_type(node->next)->next==node)
return;
list
list
int fill=0;
while(!empty()){
carry.splice(carry.begin(),*this,begin());
int i=0;
while(i counter[i].merge(carry); carry.swap(counter[i+1]); } carry.swap(counter[i]); if(i==fill) ++fill; } for(int i=1;i counter[i].merge(counter[i-1]); swap(counter[fill-1]); } list不能使用STL算法sort(),必须使用自己的sort() member function.因为STL算法sort()只接受RamdonAccessIterator,在这个函数中采用了快速排序。 详细分析如下: (1) if(node->next==node || link_type(node->next)->next==node) 这个用来判断是否是空表或者仅有一个元素,如果是其中之一,就不进行任何操作 (2)list list 这两个是新定义的lists,作为中介数据存放区。 但是list counter[0]里存放2的(0+1)次方个元素 数据中转原则是:当第i个元素即counter[i]的内容个数等于2的(i+1)次方时,就要把counter[i]的数据转移给counter[i+1]. 举个例子:假设list里面等待排序的元素为:21,45,1,30,52,3,58,47,22,59,0,58. 具体过程如下: 取出第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 其中counter[]数组的数据存放就是这样的。 list (3)carry.splice(carry.begin(),*this,begin()) 这个函数就是将begin()所指元素接合与carry.begin()处所指位置之前。也就是将list开始处的元素接合到carry开始位置处。 (4)while循环中的counter[i].merge(carry);carry.swap(counter[i++]); 第一个函数表示将carry中的元素融合到counter[i]中,第二个函数将刚融合进来的数据与先前的counter[i]中的数据进行排序,并在结束时加i加1. (5)carry.swap(counter[i])也是比较数据 (6)counter[i].merge(counter[i-1]); 这个函数将counter[i-1]中的所有元素结合到counter[i]中。 void list::splice(iterator __position, list&, iterator __i) splice:把当前列表的__i位置元素删除,保存在__position里。 void list<_Tp, _Alloc>::merge(list<_Tp, _Alloc>& __x) merge:把参数list的元素合并到当前list,参数list的内容会清空的。 swap函数,交换两个列表的元素,不要担心这个函数有负担,因为对于list来说,交换只是链表头的交换。 大致就这样分析的,应该清楚了吧。
counter[1]里存放2的(1+1)次方个元素
counter[2]里存放2的(2+1)次方个元素
counter[3]里存放2的(3+1)次方个元素,依次类推