以操作系统对任务队列的处理原理提出来优先队列这个概念,在操作系统对任务的调度中,任务队列并不是简单的FIFO形式,而是以一定的优先级被调用的,这个队列就是优先队列,调度上带有级别区分的队列。
优先队列中至少要存在两种操作:insert和deletemin,前者对应着元素入队,后者对应于出队。
可以以很多方式实现优先队列,比如链表,这样插入操作只需要O(1)的时间,但是删除最小元的时间为O(N),也可以用二叉查找树,这样两种操作的平均运行时间都为O(logN).
对于优先队列最普遍的实现是:二叉堆。
堆是一棵从左到右填满的二叉树,当然底层例外,堆可以由简单的数组来表征,对于数组位置i上的元素,它的左孩子在2i上,右孩子在2i+1上,父亲则在[i/2]上,对于堆,为了便于上述两种操作的执行,定义一种堆序性质,即:在一个堆中,对于每一个节点X,X父亲的关键字小于等于X的关键字,根结点除外。
这样我们可以给出一些基本的堆操作的算法:
1.INSERT
思路是基于一种“上滤”的策略,设堆为H,待插入的元素为X,首先在size+1的位置建立一个空穴,然后比较X和空穴的爸爸的大小,把“大的爸爸”换下来,以此推进,最后把X放到合适的位置。
2.DELETEMIN
与上面的上滤对应,这将是一种“下滤”的策略,就是逐层推进,把较小的孩子换上来,这里面有个小的问题在具体算法实现上要注意,设堆的最后一个元素是L,在推进到倒数第二层时,将导致最后一层的某个孩子被换上去而产生一个洞,这时候为了保持堆的结构,必须把最后一个元素运过去补上,此时就存在一个问题,如果L比那个孩子小的话就不能保证堆序的性质了,所以在程序中要加一个if语句来进行这个边界条件的处理,还是那句话,对于一个完整的程序,算法是重要的一方面,而对算法边界问题的处理则是更重要的一方面。
堆还有其他一些操作,不详细笔记了。
最后在说一个堆的应用:
场景:求N个元素的第k个小的元素
算法1:将N个元素排序,取出第k个 O(N)
算法2:取出N个元素的前k个进行排序,然后依次取出N-k个元素,每一个元素都和第k个元素比较,如果比它小,那就把第k个元素删除,然后将该元素插入到适当的位置,重新组成k个元素 O(NK)
算法3:使用堆,使用这N个元素构造堆(O(N)),然后进行k次deletemin(O(klogN)),总的运行时间
O(N+klogN)。当k较大时这个时间为O(klogN),对于小的k值为O(N).
阅读(2130) | 评论(2) | 转发(0) |