最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序。

平常大家写归并排序,通常写的是 递归版本。。为了效率的考虑,STL库 给出了如下的 归并排序的迭代版本.

1. MergeSort 的递归版本

首先分析下 MergeSort 的递归版本是如何工作的。递归版本代码可参考 http://blog.csdn.net/shoulinjun/article/details/19290237

考虑如下的例子,对一个长度为 8 的数组进行归并排序。

2. 迭代版本

而 迭代版本恰恰相反,是 从下往上。

为了便于明白算法的思想,我模仿 STL 库的 list sort 重新写了归并排序。文章最后给出了 STL 的源码。

// copyright @ L.J.SHOU Feb.19, 2014
// my list-sort mimicing STL's list sort
#include <iostream>
using namespace std; struct ListNode{
int val;
ListNode *next;
ListNode(int x)
:val(x), next(NULL){}
}; // merge two sorted lists into a sorted list
ListNode* merge(ListNode* &, ListNode* &); void ListSort(ListNode* & list)
{
if(list == NULL || list->next == NULL) /* 空或者1个元素的链表 */
return; ListNode *carry(NULL);
ListNode *counter[64] = {NULL}; /* 64个list, 中介数据中转站,用于模拟递归 */
int fill = 0;
while(list) {
/* insert first node of list into front of carry */
ListNode *node = list; list = list->next;
node->next = carry;
carry = node; int i = 0;
while(i < fill && counter[i]) {
counter[i] = merge(counter[i], carry);
carry = NULL; /* after merge, carry is now empty */
swap(carry, counter[i++]);
}
swap(carry, counter[i]);
if(i == fill) ++fill;
} for(int i = 1; i < fill; ++i){ /* 将 64 个 lists 归并成一个 list */
counter[i] = merge(counter[i], counter[i-1]);
counter[i-1] = NULL;
}
swap(list, counter[fill-1]);
}

代码分析:上述代码中 开了一个长度为 64 的链表数组。

第 i 个链表长度最长是 2^(i+1)。

算法的执行过程如下:

对 8 1 4 5 进行排序。

比较 递归与迭代的算法过程,可以发现两者就是互逆的过程。

现在相比大家对算法有了一个全面的认识。STL 库正是 利用 长度为 64个链表,实现了上图中的算法。

这个算法能够排序的总数是 2^65-2 个数,应该够用了。

SGI STL 的源代码(选自 STL 源码分析)如下:

// list 不能使用 STL 算法 sort ()
// 因为 STL 算法 sort() 只接受 RandomAccessIterator
// 本函数采用 mergesort
template <class T, class Alloc>
void list<T, Alloc>::sort () {
  // 以下判断,如果是空链表, 或仅有一个元素,就不进行任何操作
  if (node->next == node || link_type(node->next)->next == node)
  return;
  
  // 一些新的 lists, 作为中介数据存放区
  list<T, Alloc> carry;
  list<T, Alloc> counter[64];
  int fill = 0;
  while (!empty()) {
    carry.splice(carry.begin(), *this, begin());
    in 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]);
}

参考:1. STL源码分析, 侯捷

2.  http://blog.csdn.net/shoulinjun/article/details/19501811

STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort的更多相关文章

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

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

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

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

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

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

  4. list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考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. android:exported

    这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互.如果设置为true,则能够被调用或交互,否则不能.设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定 ...

  2. poj3159 Candies(差分约束,dij+heap)

    poj3159 Candies 这题实质为裸的差分约束. 先看最短路模型:若d[v] >= d[u] + w, 则连边u->v,之后就变成了d[v] <= d[u] + w , 即d ...

  3. soapUI参数

    点击File->New Rest Project,填入要测试的URI,确定进入编辑界面: 调整请求方式,添加请求参数,设置参数风格,这里要说一下:style有五种,QUERY是默认常用:TEMP ...

  4. python与字符集编码

    讲的比较明白的博客:http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html 以上面博文的汉为例子,汉字的GBK编码是baba, UNIC ...

  5. PHP+socket游戏数据统计平台发包接包类库

    <?php /** * @title: PHP+socket游戏数据统计平台发包接包类库 * @version: 1.0 * @author: perry <perry@1kyou.com ...

  6. linux 磁盘管理以及维护

    Linux系统中,进行频繁的读写操作,容易发送只读.以及磁盘损坏等故障.下文为其解决方案: 1.如何界定磁盘已经存在故障 方法一(界定将如下内容另存为Repair.sh然后执行即可): #!/bin/ ...

  7. HTML5表单新增属性

    1.form 原来html里面,表单里的元素应该包裹在表单里,如 <form action="login.php" method="get"> &l ...

  8. TableLayout练习

    <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android=" ...

  9. hdu 4618 Palindrome Sub-Array

    http://acm.hdu.edu.cn/showproblem.php?pid=4618 直接DP+记忆化 虽然时间复杂度看起来是300^4 但实际执行起来要远远小于这个值 所有可以水过 代码: ...

  10. NOIP 2006 解题报告

    第一题: 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定 ...