由于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表示成二进制形式,,那么子序列的形式一目了然:长度为,为0则表示没有产生长度为的子序列,反之则存在。接下来就是这些子序列如何得到的问题了。用类似加法进位的思想,每次从链表中拿出一个元素,与这些子序列进行归并,产生进位则与下一个子序列进行归并,一直到没有进位的产生。代码及注释如下:
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]);
}