STL源码——list sort:归并排序的非递归实现

由于STL中提供的sort算法是用在RandomAccessIterator上的,而list迭代器不具备随机访问的特性,所以对list进行排序不能使用algorithm中的sort算法,而应该使用list的成员函数sort。对list进行排序,最直接的想法就是用MergeSort,但是每次找中点就需要O(n),可能考虑到这点,成员函数sort并没有采用这种方式实现,而是用了非递归版的MergeSort,将待归并的子序列先保存下来,感觉有点以空间换时间的意思。(更正!由于list链表的特殊性,在进行swap和merge并没有使用额外的空间,所以最多只是牺牲了常数个list结构的空间,而元素本身并没有占用多余的空间。所以并没有牺牲空间)

主要想法就是,将已经有序的子序列先保存下来,然后再一一进行merge。那到底有多少个子序列需要merge呢?每个子序列又如何确定呢?虽然是非递归版MergeSort,但本质思想是一样的,子序列也是由一系列更小的子序列merge而来的。可以将list的长度len表示成二进制形式,《STL源码——list sort:归并排序的非递归实现》那么子序列的形式一目了然:长度为《STL源码——list sort:归并排序的非递归实现》《STL源码——list sort:归并排序的非递归实现》为0则表示没有产生长度为《STL源码——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];   //存放不同长度的子序列,每个子序列本身有序
	int fill = 0;                 //当前二进制位数
	while (!empty()) {
		//将list的表头元素转入carry中,相当于+1操作
		carry.splice(carry.begin(), *this, begin());
		int i = 0;                //当前处理的二进制位数
		//处理+1产生的进位
		while (i < fill && !counter[i].empty()) {
			counter[i].merge(carry);      //进位
			carry.swap(counter[i++]);     //保存目前的加法结果,准备处理下一位
		}
		//循环结束时,要么没有进位,要么已经处理到最高位
		carry.swap(counter[i]);  //更新当前子序列,操作后carry为空
		if (i == fill) ++fill;
	} //while

	//子序列归并
	for (int i = 1; i < fill; ++i)
		counter[i].merge(counter[i - 1]);
	swap(counter[fill - 1]);
}

点赞