240830更新:简单选择排序、堆排序
240831更新:外部排序
8.4 选择排序
8.4.1 简单选择排序
- 第i趟从i……n中选关键字最小的与L(i)交换
- 一共进行n-1趟
- 交换表示元素移动三次
8.4.2 堆排序
堆:视为完全二叉树,分大根堆与小根堆
-
堆排序的步骤(eg:大根堆)
- 建立初始大根堆——n个结点的完全二叉树
- 从第n/2↓个结点开始比较,代码中从i=2k指向左孩子开始,左右子树同时开始比较的(序列上体现的是:从末尾开始向前遍历),最后根结点,每次交换后及时下坠,最后根(堆顶元素)就是最大值
-
每下坠一层,最多对比关键字2次(左右子树)
-
树高h,结点在i层,最多下坠h-i层,对比2(h-i)次
- 堆顶元素与堆底元素交换,最大值在当前序列最后,最终得到递增序列(最先得到最大值)
- 小根堆得到递减序列(最先得到最小值)
-
大根堆的次大值一定在根的下一层
-
堆删除元素
- 被删除的元素用堆底元素替代,“从上往下”下坠,完成排序
-
堆插入元素
- 插入到堆末端,“从下往上”排序
-
堆的插入和删除都只需要移动排序即可,时间复杂度都是O(log~2~n)
排序算法 | 空间复杂度 | 元素比较次数 | 元素移动次数 | 时间复杂度 | 适用的存储结构 | 稳定性 |
---|---|---|---|---|---|---|
简单选择排序 | O(1) ;使用常数个辅助单元 | 最好:表已经有序,移动0次;最坏:不超过3(n-1) | 始终是n(n-1)/2,与序列初始状态无关 | O(n^2) | 顺序存储/链式存储;适合关键字较少 | 不稳定 |
堆排序 | O(1) ;使用常数个辅助单元 | 不超过4n,与树高h有关 | 建堆O(n);最坏下坠O(h)=O(log2n),因为树高log2n↓+1 | O(nlog2n) | 顺序存储;适合关键字较多 | 不稳定 |
8.7 外部排序
外存(磁盘)与内存间数据交换:
1.磁盘中以磁盘块为单位存储数据
2.操作系统以“块”为单位对数据进行管理
3.管理步骤:1.在内存中申请一个与“磁盘块”大小相同的缓冲区
2.将磁盘中磁盘块内数据读入缓冲区
3.在缓冲区内修改数据
4.将修改过的数据覆盖到磁盘中的磁盘块,完成外存(磁盘)数据的交换。注:此处也可以写到其他磁盘块,即修改其他磁盘块的数据
8.7.1 外部排序
-
由于内存相较外存来说很小,无法将外存所有数据一次性读入内存,需对外存内大文件进行排序时用到外部排序。外部排序:待排序的在外存,数据一部分一部分调入内存排序,进行多次内存与外存的数据交换。
-
外部排序通常采用归并排序
-
外部排序的步骤:
- 构造初始归并段(eg:m路归并)
-
内存工作区等分为2m个输入缓冲区和2个输出缓冲区,每m个输入共用一个输出
-
取外存与缓冲区等长的数据段依次读入2m个输入缓冲区
-
用内部排序算法(不一定是归并)进行排序,此时各数据段已有序,即初始归并段,依次输出到外存原位置
每个磁盘块被内存读一次,写一次
原数据段不一定是磁盘块大小,可能包含好几个磁盘块
- 第一趟归并
- 初始归并段合二为一,变为归并段,取每个归并段中第一段与缓冲区等长即初始归并段等长的数据段到内存,此时取到m个各填到一个输入缓冲区
- 取m个中最小的记录到输出缓冲区
- 输出缓冲区满即依次输出到外存
- 若某个输入缓冲区取空,立即调入对应归并段的下一个与缓冲区等长即初始归并段等长的数据段
- 得到比初始归并段大一倍的有序的归并段
- 第3……n趟归并(类上)
-
外部排序的总时间=构造初始归并段的内部排序时间+外存读/写信息时间(占大头)+多趟内部归并时间
-
优化外部排序(对应减少排序的某部分时间)
- 多路平衡归并——>减少归并趟数——>减少磁盘I/O数(减少外存读/写信息时间)
- k路平衡归并须满足:
- 最多把k个原归并段归并为一个
- 若每趟有n个原归并段,则该趟最终归并为n/m↑个归并段(n<=r)
- k路平衡归并的趟数=k叉树高度-1=logkr(r为初始归并段数)
2.败者树——>减少关键字对比次数
- 败者树:完全二叉树的一种,比之前学的二叉树更高一层,更高的这一层是根节点
- n个叶节点:对应n个归并段,即参与比较的元素;叶节点逻辑存在,实际不存在
- 内部节点:记录失败者来自哪个归并段,而不是具体的关键字
- 胜利者一直到根节点(最高层)
- 每次维护一定会从叶到根,获得胜利者
- k路归并的败者树高度 log2k↑+1;k路中取最小关键字,需要log2k次比较
- 总比较次数**(n-1)log2r**,注意与k无关,但隐含的r与k是有关的
k增大——>内存中缓冲区个数增加——>初始归并段r变小——>减少磁盘I/O次数(减少外存读/写信息时间)
弊端:若内存空间不变,缓冲区个数还是会减少,内外存交换次数增加
- 置换-选择排序——>减少生成初始归并段数目
- 初始归并段长度不确定,归并结束条件是内存工作区均被“阻塞”
- “阻塞”:但工作区有最小值但小于现有初始归并段最后一个值的时候,最小值不会输出,留在工作区中,类似“阻塞”占位置
- 最佳归并树——>减小带权路径长度WPL——>减少磁盘I/O次数
- 针对置换-选择排序后的不等长初始归并段,作归并树
- 叶结点:初始归并段
- 结点权值:归并段长度
- 叶结点到根的路径长度:归并的趟数
带权路径长度WPL=读磁盘数=写磁盘数
I/O次数=2*WPL - 总结:最佳归并树即哈夫曼树(带权路径最小)
- 构造最佳归并树的步骤:
- 补充虚段
- 虚段长度为0,在叶结点位置构造
- 初始归并段数目即度为0的数目n0
- (n0-1)%(k-1)=u ;u=0不需要构造虚段,u≠0构造**k-u-1个虚段放在叶结点
- 度为k的结点nk=(n0-1)/(k-1),nk=0即u=0,即正则k叉树不需要虚段
- 构造k叉哈夫曼树
- 补充虚段