当前课程知识点:数据结构(下) > 第十二章 排序 > (b3)选取:通用算法 > 12b3-1: 尝试
在讨论过众数 以及特殊情况下
中位数的计算方法以后
我们接下来针对一般性的选取问题
介绍优化的通用算法
既然选取问题的查找目标
就是在整个数据集中
按大小次序秩为k的那个元素
所以最直接不过的方法
莫过于首先对整个数据集做一次排序
此后我们只需从首元素开始
依次向后移动
当累计移动了K步之后
我们也就自然的抵达了查找的目标
然而很遗憾这里首先需要做一趟排序
我们知道在最坏情况下
我们不得不为此花费nlogn的时间
对于这样一种性能
我们是不能满意的
正如我们马上就要看到的
实际上存在更为优化的算法
当我们听到选取这个词时
获取很自然的会想起堆结构
没错
堆结构主要功能也是在做
某种意义上的选取
也就是选取其中的极值元素
从这个意义上讲
选取极值就是选取问题的一个特例
而反过来选取问题
也是getmax之类操作的
一般化推广
那么是否有可能借助堆结构
来实现高效的选取操作呢
沿着这个思路你或许会想出
如这个图所示的一种方法
具体来说我们需要首先将
所有的N个元素
组织为一个堆
当然这里我们需要的
是一个小顶堆
也就是说堆顶元素是全局的极小值
因此每当我们调用一次delmin接口
就可以摘出当前的极小值
就全局而言
第一次调用将会摘出全局的最小元
也就是秩为0者
而接下来的第二次调用
将会得到全局秩为1者
因此在我们连续的
对这个接口调用K次之后
整个堆的规模
将变为N减K
而此时的堆顶也就应该恰好是
全局秩为K的那个元素
就第一步预处理
也就是建堆操作而言
我们的性能还不差
我们可以直接调用弗洛伊德算法
也就是说为此我们只需花费
线性的时间
然后此后对delmin接口的调用
累计时间却很长
具体来说
我们每次都需要花费logn的时间
而总共需要调用k次
也就是说只要K在数量集上与N同阶
这个方法的渐进性能
将与全排序的方法
没有什么实质区别
当然我们也可以尝试
利用堆的其他方法
比如一种可行的方案是
我们首先从数据集中
任选出K个元素
并将它们组织为一个大顶堆
接下来对于剩余的N减k个元素
我们分别调用一次insert的操作
将它插入堆中
然后随机调用一次delMax接口
摘除掉新的堆顶
对于这样的每一步迭代
如果此前堆的规模为K
那么在执行insert的操作之后
它的规模将变为k加1
而在随后立即执行过delmax操作之后
堆的规模又会从K加1
重新恢复为K
请注意当每一次这个堆的规模
从K增加到K加1时
对应的堆顶元素
都是迄今为止发现的秩为K的那个元素
因此当所有元素都经过如此处理之后
当这个堆的规模最后一次达到K加1时
堆顶元素也就是全局秩为K的那个元素
由此可见按照这种方法
我们的确可以完成选取的任务
然而同样很遗憾
它的时间复杂度
依然不能得到有效的控制
具体的为了构件初始的堆
我们这里同样只需线性的时间
然而在随后的N减K次迭代中
无论插入或者删除
我们都需要多达logk的时间
当然如果k非常小
或者反过来非常大
这个算法的性能将接近于线性
然而当K的取值接近中间范围时
这个算法的复杂度
又将重新回到nlogn
当然利用堆结构
来实现选取功能的方法
还有很多
我们这里再来看其中的一种
具体来说这里我们将使用两个
而不是一个堆
首先我们要从数据集中
任取K个元素
构件一个大顶堆h
相应的我们要将剩余的n减k个元素
组织为一个小顶堆g
接下来我们需要反复的比较
这两个堆的堆顶
只要H大于g
我们就令二者互换位置
然后分别通过一次(下律)
将这两个堆重新复原
这个迭代将持续进行下去
直到最终h不再大于g
而一旦达到这种状态
对于g的顶元素
也就是我们要查找的目标
因为对于此时的这个元素来说
总共有k个元素不大于它
同时有n减K再减1个元素
不小于它
尽管这种方法的构思非常精巧
但是同样的
它在最坏情况下的复杂度
依然不能得到有效的控制
请你在课后
对照我们这里所给的标注
独立的验证这一结论
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-OJ系统说明
--关于OJ
--1-注册与登录
--2-界面与选课
--3-提交测试
-关于课程教材与讲义
--课程教材与讲义
-关于讨论区
--关于讨论区
-微信平台
--html
-PA晋级申请
--PA晋级
-(a)概述
--07A-1 纵览
--07A-5 接口
-(a)概述--作业
-(b1)BST:查找
-第七章 二叉搜索树--(b1)BST:查找
-(b2)BST:插入
-(b2)BST:插入--作业
-(b3)BST:删除
-第七章 二叉搜索树--(b3)BST:删除
-(c)平衡与等价
-(c)平衡与等价--作业
-(d1)AVL树:重平衡
-第七章 二叉搜索树--(d1)AVL树:重平衡
-(d2)AVL树:插入
-(d2)AVL树:插入--作业
-(d3)AVL树:删除
-(d3)AVL树:删除--作业
-(d4)AVL树:(3+4)-重构
-(d4)AVL树:(3+4)-重构--作业
-本章测验
--章节测验
-(a1)伸展树:逐层伸展
--习题
-(a2)伸展树:双层伸展
--习题
-(a3)伸展树:算法实现
--习题
-(b1)B-树:动机
--习题
-(b2)B-树:结构
--习题
-(b3)B-树:查找
--习题
-(b4)B-树: 插入
--习题
-(b5)B-树: 删除
--习题
-(xa1)红黑树:动机
--习题
-(xa2)红黑树:结构
--习题
-(xa3)红黑树:插入
--习题
-(xa4)红黑树:删除
-本章测验
--习题
-(b)散列:原理
--09B-3 数组
--09B-4 原理
--09B-5 散列
--09B-6 冲突
--习题
-(c)散列:散列函数
--习题
-(d1)散列:排解冲突(1)
--习题
-(d2)散列:排解冲突(2)
--习题
-(e)桶/计数排序
--习题
-本章测验
--本章测试
-(a1)需求与动机
--习题
-(a2)基本实现
--习题
-(b1)完全二叉堆:结构
--习题
-(b2)完全二叉堆:插入与上滤
--习题
-(b3)完全二叉堆:删除与下滤
--习题
-(b4)完全二叉堆:批量建堆
--习题
-(c)堆排序
--习题
-(xa1)左式堆:结构
--习题
-(xa2)左式堆:合并
--习题
-(xa3)左式堆:插入与删除
-本章测验
--本章测试
-(a)ADT
--习题
-(b1)串匹配
--习题
-(b2)蛮力匹配
--习题
-(c1)KMP算法:从记忆力到预知力
--习题
-(c2)KMP算法:查询表
--习题
-(c3)KMP算法:理解next[]表
--习题
-(c4)KMP算法:构造next[]表
--习题
-(c5)KMP算法:分摊分析
--习题
-(c6)KMP算法:再改进
-(d1)BM_BC算法:以终为始
-(d2)BM_BC算法:坏字符
-(d3)BM_BC算法:构造bc[]
-(d4)BM_BC算法:性能分析
-(e1)BM_GS算法:好后缀
-(e2)BM_GS算法:构造gs表
-(e3)BM_GS算法:综合性能
-(f1)Karp-Rabin算法:串即是数
-(f2)Karp-Rabin算法:散列
-本章测验
--本章测试
-(a1)快速排序:算法A
-- 12a1-5: 实例
--习题
-(a2)快速排序:性能分析
--习题
-(a4)快速排序:变种
-(b1)选取:众数
-(b3)选取:通用算法
--习题
-(c1) 希尔排序:Shell序列
--习题
-(c2)希尔排序:逆序对
-本章测验
--本章测试