4.3list
list::sort()
分析
list::sort()
使用的是归并。但是对于传统的归并,list很难找中点分治。也就是对于自下而上归并开销极大。
但是可以换个思路,对序列进行自左向右归并。每拿到两个小归并层的排序结果就立马合并成一个大归并。这样就避免了在传统归并排序中,对于list很难去寻找归并切分点的难题。
所以算法妙还是妙在对counter这个循环不变量的定义维护。
传统的归并
list::sort中的归并
代码
template<class T,class Alloc>
void list<T,Alloc>::sort() {
//序列为空则返回
if(node->next == node || link_type(node->next)->next == node)
return;
list<T,Alloc> carry;
list<T,Alloc> counter[64];//最多可处理2^64-1个元素长的序列
int fill = 0;//归并最大层数,初始为0,即序列长度为1
while(!empty()) {
// 每次取出一个元素到carry头
carry = splice(carry.begin(),*this,begin());
//试图归并
int i = 0;
//!counter[i].empty()则是用来判断另一端归并结果的。
//如果非空,则说明另一侧序列长为2^i的另一半排序结果已产生。
//现在应该拼接两个half,即当前的carry(也就是新来的couter[i-1])
//特别的,当i=0时,则是carry与counter[0]合并。
//产生序列长为1的排序结果,那么则说明最后应该把这个结果放在索引1层(可处理2个元素排序的一层)
while(i < fill && !counter[i].empty()) {
counter[i].merge(carry);
carry.swap(counter[i++]);
}
carry.swap(counter[i]); //将一半排序序列存储到索引i位。等待下一次归并。
if (i == fill) ++fill; // 层数不够用了。该提高一层,扩大一倍可排序容量。
}
//序列长度并不一定为2^i倍数。
//那么就会造成有部分排序结果卡在中间某层,缺少元素,无法送到更高层。
//那么这显然是不对的。所以最好要处理索引小的几个counter,
//但庆幸的是,他们一定有序,那么merge一下就好了。
for(int i = 1;i < fill;++i) {
counter[i].merge(counter[i-1]);
}
swap(counter[fill-1]);
}