数据结构与算法 代码整理:外排序法

外排序原理:

外排序就是能够处理极大量数据的排序算法。通常来说,外排序处理的数据不能一次性装入内存,只能放在读写较慢的外存储器(eg.硬盘)上,通常采用“排序-归并”策略。

算法性能分析:

参考:http://blog.chinaunix.net/uid-25324849-id-2182916.html

多路归并的实现:

败者树是树形选择排序的一种变形。若在双亲结点中记下刚进行完比赛中的败者,而让胜者去参加高一级的比赛,便可得到一颗“败者树”。

如下图a中,在败者树中,根节点ls[2]的双亲结点ls[1]为冠军,指示各归并段中的最小关键字元素在第4段,

结点ls[5]指示败者为b5,胜者为b4,b4将在下一次比赛ls[1]中和b1比较;结点ls[1]指示败者为b1,胜者为b4,与另一场比赛的胜者b2进行比较。

 在选得最小关键字元素后,只需修改对应叶子结点的值(b4),使其同一个归并段的下一个元素作为该胜出叶子结点的新的关键字,然后从该节点向上与双亲结点所指的关键字比较,败者留在该双亲,胜者继续向上直至树根的双亲,如图b所示。

算法思想用python实现如下:

 1 # 外排序——多路归并排序法
 2 # 败者树算法
 3 
 4 # 模拟磁盘中已排序区域,并在文件最后添加一个极大数作为判断结束的哨兵值
 5 # 注意,由于从列表后面删除元素要快于从前面删除,所以示例中的区域采用倒序排列,把第一个关键字放在最后
 6 B = {1:[999,    16,15,10],
 7      2:[999,    20,18,9 ],
 8      3:[999,    40,22,20],
 9      4:[999,    25,15,6 ],
10      5:[999,    48,37,12]}
11 k = len(B)   # k路归并
12 merge_result = []     # 目标归并段
13 
14 
15 # k 路归并处理程序
16 # 利用败者树ls将编号从1到k的k个输入归并段元素归并到输出端
17 # b[1]到b[k]为败者树上的k个叶子结点,分别存放k个输入归并段中当前元素的关键字
18 b = [0]*(k+1)       # 起点从1开始,第0位空着
19 ls = [0]*(k+1)      # 由于败者树是完全二叉树(不含叶子),则该树的结点数即叶子个数(归并路数),可采用顺序储存结构
20 
21 
22 def K_Merge():
23     for i in range(1,k+1):      # 分别从k个输入归并中读入该段中的第一个关键字
24         input(i)                # 保存到叶子节点中
25     CreaterLoserTree()          # 根据当前关键字b建立败者树ls
26     while b[ls[1]]!=999:        # 当冠军值非最大的哨兵值(存在未被归并的元素)时,归并继续
27         q = ls[1]               # q指示当前最小关键字所在的归并段
28         output(q)               # 将编号为 q 的段中最前面关键字写入输出归并段
29         input(q)                # 从编号为 q 的段中读入下一个关键字
30         AdjustLoserTree(q)      # 根据当前冠军,调整败者树,选择新的最小关键字
31 #     output(1)                   # 将喊最大关键字的元素写入输出段(假设选择最大关键字作为哨兵的话,不过这里不需要)
32     
33 # 建立败者树ls
34 # 已知从b[1]到b[k]为完全二叉树ls的叶子结点存有k个关键字,沿从叶子到根的k条路径将ls调整为败者树
35 def CreaterLoserTree():
36     # 假设败者树上存放的都是最小键值(所在的序号),则可以对每个叶子节点进行测试
37     # 使当前结点的键值上升为新的败者,以替换掉原来树节点的位置
38 
39     minval = b[1]
40     for i in range(2,k):        # 先从第一批叶子结点中找到拥有最小的键值所在的段
41         if b[i]<minval:
42             minval = b[i]; minidx = i
43     
44     for i in range(1,k+1):      
45         ls[i] = minidx          # 设置败者初值,为最小键值所在的段序号
46     
47     for i in range(k,0,-1):
48         AdjustLoserTree(i)      # 依次从b[1],b[2],...,b[k]出发调整败者树
49 
50 # 选择最小关键字后,从叶到根调整败者树,选出下一个最小关键字
51 # 沿从叶子节点b[s],到根节点ls[0]的路径调整败者树
52 def AdjustLoserTree(s):
53     t = (s+k+1)/2               # ls[t]是b[s]的父结点 (+1是为了四舍五入)
54     while t > 1:
55         if b[s] > b[ls[t]]:     # 败者树的结点中存放的是比较后较大的关键字所在的段序号
56             s,ls[t] = ls[t],s   # 当b[s]被击败后,将s指示向新的胜利者,并将败者留着当前的内结点中
57         t = (t+1)/2             # 上移到上一个父结点
58     ls[1] = s
59 
60 
61 
62 # 模拟外存读入函数,从i所指向的区域读取第一个关键字
63 def input(i):
64     b[i] = B[i][-1]
65 # 模拟输出归并函数,从i所指向的区域中读取第一个关键字,并输出到归并段
66 def output(i):
67     merge_result.append(B[i].pop())
68 
69 
70 if __name__=="__main__":
71     pass
72     K_Merge()
73     print merge_result
74     #>>> [6, 9, 10, 12, 15, 15, 16, 18, 20, 20, 22, 25, 37, 40, 48]
原文地址:https://www.cnblogs.com/hanahimi/p/4729274.html