STL源码剖析第四章笔记

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]);
}
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值