STL中有一个std::sort算法,但它是不支持std::list的,因为list不提供RandomIterator的支持,但list自己提供了sort算法,把list的元素按从小到大的方式来排序,代码长度到不长,但真是难以读懂,后来扣持了一下午终于搞明白了,贴个总结上来。

list::sort的代码如下(sgi stl):

  1. template <class _Tp, class _Alloc>
  2. void list<_Tp, _Alloc>::sort()
  3. {
  4. // Do nothing if the list has length 0 or 1.
  5. if (_M_node->_M_next != _M_node && _M_node->_M_next->_M_next != _M_node) {
  6. list<_Tp, _Alloc> __carry;
  7. list<_Tp, _Alloc> __counter[64];
  8. int __fill = 0;
  9. while (!empty()) {
  10. __carry.splice(__carry.begin(), *this, begin());
  11. int __i = 0;
  12. while(__i < __fill && !__counter[__i].empty()) {
  13. __counter[__i].merge(__carry);
  14. __carry.swap(__counter[__i++]);
  15. }
  16. __carry.swap(__counter[__i]);
  17. if (__i == __fill) ++__fill;
  18. }
  19. for (int __i = 1; __i < __fill; ++__i)
  20. __counter[__i].merge(__counter[__i-1]);
  21. swap(__counter[__fill-1]);
  22. }
  23. }

行数的确不多,但还真麻烦,我先说一下他是怎么实现的,但具体为什么这么做,我不知道。

比如我们的list里有如下几个需要排序的元素:21,45,1,30,52,3,58,47,22,59,0,58。

排序的时候怎么做,我们先定义若干中转list在上述代码中定义了64个元素的数组

list<_Tp, _Alloc> __counter[64]; 其中里边存什么呢?他们都是用来中转用的

__counter[0]里存放2(0+1)次方个元素

__counter[1]里存放2(1+1)次方个元素

__counter[2]里存放2(2+1)次方个元素

__counter[3]里存放2(3+1)次方个元素,依次类推

那又是怎么个存放方法呢?一个指导原则就是当第i个元素即__counter[i]的内容个数等于2(i+1)次方时,就要把__counter[i]的数据转移给__count[i+1]。

具体过程如下:

取出第1个数21,放到__counter[0]里,这时__counter[0]里有一个元素,小于2,继续

__counter[0]: 21

__counter[1]: NULL

取出第2个数45,放到__counter[0]里(不是简单的放,而是排序放,类似两个list做merge),这时__counter[0]里有2个元素了,需要把这两个元素转移到__counter[1].

__counter[0]: NULL

__counter[1]: 21,45

取出第3个数1,放到__counter[0]里,__count[0]与__count[1]都小于规定个数

__counter[0]: 1

__counter[1]: 21,45

取出第4个数30,放到__counter[0]里,这时__counter[0]的个数等于2了,需要转移到__counter[1]里

__counter[0]: NULL

__counter[1]: 1,21,30,45

但这时__counter[1]里的个数又等于4了,所有需要把__counter[1]的值转移到__counter[2]里,

__counter[0]: NULL

__counter[1]: NULL

__counter[2]: 1,21,30,45

然后取出52,放入__counter[0]

__counter[0]: 52

__counter[1]: NULL

__counter[2]: 1,21,30,45

然后取出3,放入__counter[0]

__counter[0]: 3,52

__counter[1]: NULL

__counter[2]: 1,21,30,45

这时候需要转移

__counter[0]: NULL

__counter[1]: 3,52

__counter[2]: 1,21,30,45

然后取58

__counter[0]: 58

__counter[1]: 3,52

__counter[2]: 1,21,30,45

然后取47

__counter[0]: 47,58

__counter[1]: 3,52

__counter[2]: 1,21,30,45

需要转移

__counter[0]: NULL

__counter[1]: 3,47,52,58

__counter[2]: 1,21,30,45

还需要转移

__counter[0]: NULL

__counter[1]: NULL

__counter[2]: 1,3,21,30,47,45,52,58

还需要转移

__counter[0]: NULL

__counter[1]: NULL

__counter[2]: NULL

__counter[3]: 1,3,21,30,47,45,52,58

然后再取59

__counter[0]: 59

__counter[1]: NULL

__counter[2]: NULL

__counter[3]: 1,3,21,30,47,45,52,58

然后取0

__counter[0]: 0,59

__counter[1]: NULL

__counter[2]: NULL

__counter[3]: 1,3,21,30,47,45,52,58

需要转移

__counter[0]: NULL

__counter[1]: 0,59

__counter[2]: NULL

__counter[3]: 1,3,21,30,47,45,52,58

最后取58

__counter[0]: 58

__counter[1]: 0,59

__counter[2]: NULL

__counter[3]: 1,3,21,30,47,45,52,58

脑算流程总算完了,但代码还是很难理解,先看一个几个相关的函数吧

1.splice:把当前列表的__i位置元素删除,保存在__position里

[cpp] void list::splice(iterator __position, list&, iterator __i)  
  1. void list::splice(iterator __position, list&, iterator __i)

2.merge:把参数list的元素合并到当前list,参数list的内容会清空的

[c-sharp] void list<_Tp, _Alloc>::merge(list<_Tp, _Alloc>& __x)

/* Written     By     MaiK */

STL中的list被实现为环状的双向链表,设置一个“哨兵”node作为end( )。鉴于list的内存分配模型,list不能使用通用的标准sort算法,而是实现自身的sort,但是list有自己的成员函数sort()可供其自身调用,其实际模型是基于合并排序的。普通的mergesort直接将待排序的序列一分为二,然后各自递归调用mergesort,再使用Merge算法用O(n)的时间将已排完序的两个子序列归并,从而总时间效率为n*lg(n)。(mergesort是很好的排序算法,绝对时间很小,n*lg(n)之前的系数也很小,但是在内存中的排序算法中并不常见,我想可能主要还是因为耗空间太多,也是O(n)).

不过list_sort所使用的mergesort形式上大不一样:将前两个元素归并,再将后两个元素归并,归并这两个小子序列成为4个元素的有序子序列;重复这一过程,得到8个元素的有序子序列,16个的,32个的。。。,直到全部处理完。主要调用了swap和merge函数,而这些又依赖于内部实现的transfer函数(其时间代价为O(1))。该mergesort算法时间代价亦为n*lg(n),计算起来比较复杂。list_sort中预留了 64个temp_list,所以最多可以处理2^64-1个元素的序列,这应该足够了。

/* Written     By    Lamar */

类似2进制,每一次进位都是相邻高位数值的一半,所以是类2进制地。例如8,低位4满之后会进4个到8的。

STL源码分析----神奇的 list 的 sort 算法实现的更多相关文章

  1. STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort

    最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...

  2. STL源码分析《4》----Traits技术

    在 STL 源码中,到处可见 Traits 的身影,其实 Traits 不是一种语法,更确切地说是一种技术. STL库中,有一个函数叫做 advance, 用来将某个迭代器(具有指针行为的一种 cla ...

  3. STL源码分析《3》----辅助空间不足时,如何进行归并排序

    两个连在一起的序列 [first, middle) 和 [middle, last) 都已经排序, 归并排序最核心的算法就是 将 [first, middle) 和 [middle, last) 在  ...

  4. STL源码分析读书笔记--第二章--空间配置器(allocator)

    声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...

  5. STL 源码分析《2》----nth_element() 使用与源码分析

    Select 问题: 在一个无序的数组中 找到第 n 大的元素. 思路 1: 排序,O(NlgN) 思路 2: 利用快排的 RandomizedPartition(), 平均复杂度是 O(N) 思路 ...

  6. stl源码分析之allocator

    allocator封装了stl标准程序库的内存管理系统,标准库的string,容器,算法和部分iostream都是通过allocator分配和释放内存的.标准库的组件有一个参数指定使用的allocat ...

  7. STL源码分析与实现-stl_list容器

    1. stl_list 介绍 今天我们来总结一下stl_List, 通过之前介绍单链表的文章,其实对链表的基本操作已经十分熟悉了,那对于stl_list,无非就是链表结构不一样,至于其中的增删改查的细 ...

  8. STL 源码分析六大组件-allocator

    1. allocator 基本介绍 分配器(allocator))是C ++标准库的一个组件, 主要用来处理所有给定容器(vector,list,map等)内存的分配和释放.C ++标准库提供了默认使 ...

  9. STL源码分析之迭代器

    前言 迭代器是将算法和容器两个独立的泛型进行调和的一个接口. 使我们不需要关系中间的转化是怎么样的就都能直接使用迭代器进行数据访问. 而迭代器最重要的就是对operator *和operator-&g ...

随机推荐

  1. Node.js 开发模式(设计模式)

    Asynchronous code & Synchronous code As we have seen in an earlier post (here), how node does th ...

  2. saiku-添加数据源以及保证数据源的一致性

    采用任何一种添加数据源的方式都不能保证数据源的一致和完整,所以需要两种结合来管理数据源 1.通过saiku的管理台添加数据源 ① 第一种方式:schema和ds都在管理台添加 1)上传schema文件 ...

  3. uva------Help is needed for Dexter(11384)

    Problem H Help is needed for Dexter Time Limit: 3 Second Dexter is tired of Dee Dee. So he decided t ...

  4. ztree 展示

    1. controller@RequestMapping("/function.htm") public String function(HttpSession session, ...

  5. js基础练习---图片无缝左右滚动效果(主要以复制删除为主)

    昨天闲来没事 看了下图片效果  发现这个方法j 就自己模仿下 上代码  当中有很多的纰漏 请大神们多多指教一二? <script type="text/javascript" ...

  6. 如何为Kafka集群选择合适的Partitions数量

    转载:http://blog.csdn.net/odailidong/article/details/52571901 这是许多kafka使用者经常会问到的一个问题.本文的目的是介绍与本问题相关的一些 ...

  7. Java如何将控制台上的结果保存到文件

    无论是二进制数据还是字符数据(文本数据),都可以用文件输出流java.io.FileOutputStream,以字节流的方式保存到指定文件. package test; import java.io. ...

  8. Servlet作业--实现注册和登录

    1.注册页面  zhuce.html <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " ...

  9. QQ2013登录报文简单分析(不可用于非法用途)

    [NO.1 2013-05-08 00:31:16 046 SEND 115字节]02 31 03 08 25 27 B5 88 6F 91 D2 03 00 00 00 0101 01 00 00 ...

  10. 二模 (1) day1

    第一题: 题目大意:给出N(N<=50)个小于1000的正整数Ai,和一个正整数max,和一个整数cur,从前往后依次对每个Ai,可以让cur+Ai 或者 cur-Ai,但是结果不能大于max, ...