STL_list_sort算法复杂度分析

在看jjhou的STL源码那本书的时候,看到list::sort函数的时候费了一点劲。

主要是书中注释居然写的是quick sort。导致我一直在琢磨这怎么就是个quick sort了?

 

list::sort代码如下:

template <class _Tp, class _Alloc>
void list<_Tp, _Alloc>::sort()
{
  // Do nothing if the list has length 0 or 1.
  if (_M_node->_M_next != _M_node && _M_node->_M_next->_M_next != _M_node) {
    list<_Tp, _Alloc> __carry;
    list<_Tp, _Alloc> __counter[64];
    int __fill = 0;
    while (!empty()) {
      __carry.splice(__carry.begin(), *this, begin());
      int __i = 0;
      while(__i < __fill && !__counter[__i].empty()) {
        __counter[__i].merge(__carry);
        __carry.swap(__counter[__i++]);
      }
      __carry.swap(__counter[__i]);        
      if (__i == __fill) ++__fill;
    }

    for (int __i = 1; __i < __fill; ++__i)
      __counter[__i].merge(__counter[__i-1]);
    swap(__counter[__fill-1]);
  }
}

 

以上代码来源与SGI STL!

该算法是属于merge sort归并排序。一般情况下看到的连续内存区间的归并排序大多使用递归,不使用递归就要自定义记忆表保存计算过程中的计算结果,因为采取二分的分割原则,保存结果只需要O(N)的额外空间用于归并就可以了。

不过一般是采取递归的方法,比较容易理解。

 

因为list节点独立,在配合splice函数可以让list中的节点移动很方便。采取归并排序不需要O(N)的空间就可以完成。

这里定义了__counter[64]有64个桶用于保存临时的已排序元素链。

 

__carry.splice(__carry.begin(), *this, begin());从待排序链表去一个元素。

 

根据while循环的意义可以知道,碰到有元素的桶就和该桶合并,合并后swap到__carry中,一致进行直到碰到空桶就把当前停止并把__carry链表swap到该桶里保存。(注意:__fill表示已用桶的最大标号 + 1, 在循环里控制循环不要走的太远了)

 

这样在for循环里一直进行直到元素取完为止。

 

第二个for循环是整个排序算法的终结,在这里将所有的桶通过merge链接到一起,最后在swap到当前list中。

 

复杂度:

假设64个桶的标号从1..64,那么第一个桶中可容纳1个元素,第二个桶中为2个元素,第三个同种为4个元素,以此类推第n个桶中可容纳2^n个元素。所以O(N) = 2 * O(N / 2) +  N,解该表达式,可得到O(N) =  O(N * logN)

 

注意:该文中出现的桶不是桶式排序的桶,桶是一个链表的形象标识。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值