第六章节 优先队列

一般发送到打印机的作业 放到队列中,但这并不一定是最好的做法 ,比如可能有一个作业很重要,可以先打印,这个时候,就可以用到优先队列。

另外,短的作业一般应该首先完成 ,因此,在运行的程序中,短的作业有更高的优先权。

一、模型

优先队列一定要有的两个操作:insert  ,deleteMin(找到并删除最小的).

其中,insert相当于队列中的enqueue, deleteMin类似于队列中的dequeue.

二、一些简单的实现 

有几种简单的办法可以实现

  • 使用简单的链表

在表头以O(1)进行插入,遍历以O(N)删除最小的.或者使链表保存有序的状态,则insert 要O(N), 而deleteMin要O(1).

  • 使用二权查找树

deleteMin/ insert复杂度都是 O(logN).但是使用插找树有些过份了,因为它还支持很多别的工作,此外,在删除的最坏的情况下,会有失去平衡等问题。

  • 二叉堆

不需要用到链,支持最坏的情形时O(logN),且插入操作平均用时为常数时间。

三、二叉堆

对于优先队列的实现时,使用的很普遍。和二叉查找树一样,堆也有两个性质,结构性与堆序性。对堆的操作可能破坏其中的一个,因此,堆 的操作一定要

到堆的所有性质都满足才终止。

  • 性质结构 

堆是一个完全填满的二叉树。一个完全二叉树可以用数组表示。对一个位置i的元素,左儿子在2i,右儿子在2i+1.父亲在i/2取整数。因此我们不用使用链,而且遍历很简单。唯一的问题是

要先估计大小 。

一个堆结构由一个comparable对象数组与一个代表当前堆大小的整数组成。如下,是一个堆

package charpter6;

public class BinaryHeap <Anytype extends Comparable<? super Anytype>>{
	private static final int DEFULT_CAPACITY=10;
	private int currentSize ;
	private Anytype [] array ;

	public BinaryHeap(){

	}
	public BinaryHeap(int capacity){

	}
	public BinaryHeap(Anytype [] items){
		currentSize = items.length ;
		array = (Anytype[]) new Comparable[(currentSize+2)*11/10];

		int i =1;
		for (Anytype item : items)
			array[i++] = item;
		buildHeap() ;
	}
	//这里不用递归
	public void insert(Anytype x ){
		if (currentSize == array.length-1)
			enlargeArray(array.length*2-1);

		//percolate up
		int hole = ++ currentSize ;
		for (; hole>1&&x.compareTo(array[hole/2])<0; hole/=2){
			array[hole] = array[hole/2] ;
		}
		array[hole] = x ;
	}
	public Anytype findMin () throws Exception{
		if (isEmpty())
			throw new Exception() ;

		return array[1] ;
	}
	public Anytype deleteMin () throws Exception{
		if (isEmpty())
			throw new Exception() ;

		Anytype minItem = findMin() ;
		array[1] = array[currentSize--] ;// 先将array[currentSize]移动到空穴,再将currentSize-1
		percolateDown(1) ;

		return minItem  ;
	}
	public boolean isEmpty (){
		return false ;
	}
	public void makeEmpty (){

	}

	private void percolateDown (int hole){
		int child ;
		Anytype temp = array[hole] ;

		for (; hole*2<= currentSize; hole= child){
			child = 2* hole; //左儿子
			//有右儿子时,选出两个较小的一个
			if (child!= currentSize&& array[child+1].compareTo(array[child])<0){
				child++ ;
			}
			//下滤
			if (array[child].compareTo(temp)<0){
				array[hole] = array[child] ;
			}
			else {
				break ;
			}
		}
		array[hole] = temp ;
	}
	//O(N),这个操作从下而上,不能反
	private void buildHeap (){
		for (int i= currentSize/2; i>0;i--)
			percolateDown(i) ;
	}
	private void enlargeArray (int newSize){

	}

}

  

  • 堆性质

一个堆中,对于每一个节点X, X的父亲的关键字小于或者等于X中的关键字。因此,最小的元素就在根处。

堆的基本操作

insert 

在下一个可用的位置放一个空穴:

如果 X可以放在这个空穴中,则完成。

如果不可,将空穴的父节点放到 空穴中,这样空穴就上移,直到X能放入空穴为止。

这种操作是上滤。新的元素在堆中上滤直到找到正确的位置。

如果插入的是最小元素,则要上滤到根处,将用时O(logN),平均来看,性能好很多,插入一次只要2.6次比较。性能好很多。

deleteMin 

找到最小元素是简单的,但是删除比较复杂。

当删除一个最小元素时,根处出现 一个空穴,由于现在堆少了一个元素,因此堆中的最后一个元素X要移动到一个地方。

如果X可以直接放到空穴中,删除完成 。

如果不可以,将空穴的两儿子中小的移动到空穴,这样空穴下滤一层,重复上过程直到X可放到空穴中。

因此 ,做法就是将X放到沿着根开始,有最小儿子的一条路径 上的一个正确 的路径上。

对于一个节点如果只有一个儿子,我们要进行附加的测试,

这种操作的最坏情况运行时间为O(logN),平均来说,也是O(logN).

其它操作

事实上一个堆所蕴含的有序信息很少,如果不对整个堆进行线性搜索,是没有办法找到任何特定的关键字的。

buildHeap操作

可以将N个元素insert到一个空堆中,每一个insert将花费O(1)的平均时间和O(logN)的最坏时间,因此整个过和将花费O(N),更不是O(NlogN)。这是一种特殊的指令,没有

别的操作干扰。

一般的算法 是将N项以任意的顺序放到树中,保持结构特性,然后再percolatedown (i),以构造一个堆序的树。

四、优先队列的使用

选择问题

从N个元素中找出第k个最大的元素。下面给出两个在 k=N/2时,最坏以O(NlogN)运行的算法 。

算法1

只考虑找到第k个最小的元素,将N个元素读入数组,进行buildHeap算法 ,最后,进行k次deleteMin,得到结果。使用的时间为

O(N+k*logN)。如果 k很大,则为O(klogN),如果 k=N/2则为O(NlogN).如k=N,则相当于给N个元素进行了排序。

算法2

我们维持一个大小为k的堆,根元素就是这个小集合中最小的,再读入一个新的元素,与根进行比较。(略)。

除了不能进行find操作,堆最大的缺点是将两个堆合并是一个很困难的操作。下面讨论几个可以支持以O(NlogN)的时间进行merge的数据结构。

时间: 2024-10-10 20:58:49

第六章节 优先队列的相关文章

html学习第一天笔记——第六章节

<input type="reset" value="重置"> 使用重置按钮,重置表单信息<input type="submit" value="提交"> 使用提交按钮,提交数据<input type="radio/checkbox" value="值" name="名称" checked="checked"/>

Netty in Action (十五) 第六章节 第一部分 ChannelHandler和ChannelPipeline

本章内容包括: 1)ChannelHandler和ChannelPipeline的APIs 2)检测内存泄漏 3)异常处理 在之前的一个章节中,我们学习了ByteBuf,Netty的数据容器,在这个章节中,我们将讲解Netty的数据流和对应的处理组件,然后我们将我们已经学过的所有组件整合在一起 你已经知道多个ChannelHandler可以被链式的放入ChannelPipeline来将所有的处理逻辑组织在一起,我们将学习包涵这些有关类的很多用户案例和他们之间的对应关系------ChannelH

Netty in Action (十六) 第六章节 第二部分 ChannelHandlerContext和异常处理

6.3 Interface ChannelHandlerContext 一个ChannelHandlerContext代表了一个ChannelHandler和ChannelPipeline之间的关系,ChannelHandlerContext创建于ChannelHandler被载入到ChannelPipeline的时候,ChannelHandlerContext主要功能是管理在同一ChannelPipeline中各个ChannelHandler的交互 ChannelHandlerContext有

算法导论第六章优先队列(二)

优先队列可以说是堆的一个非常重要的应用,和堆对应,优先队列也分最小优先队列和最大优先队列. 优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中每一个元素都有一个关键字(key),关键字赋予了一个元素的优先级,故名为优先队列.之所以用堆来实现优先队列,我想最大的原因是堆很容易对元素按关键字进行排序. 优先队列的应用: 最大优先队列:其中最为典型的就是“共享计算机系统的作业调度”,通过记录各个作业的优先级,来调度一个作业的执行.删除和插入等操作. 最小优先队列:可以被用于“基于事件驱动的模

第六章——优先队列

1.概述 队列是一种满足先进先出(FIFO)的数据结构,数据从队列头部取出,新的数据从队列尾部插入,数据之间是平等的,不存在优先级的.这个就类似于普通老百姓到火车站排队买票,先来的先买票,每个人之间是平等的,不存在优先的权利,整个过程是固定不变的.而优先级队列可以理解为在队列的基础上给每个数据赋一个权值,代表数据的优先级.与队列类似,优先级队列也是从头部取出数据,从尾部插入数据,但是这个过程根据数据的优先级而变化的,总是优先级高的先出来,所以不一定是先进先出的.这个过就类似于买火车票时候军人比普

Hadoop学习之第六章节:Hbase配置安装

1.安装Hbase 1)下载,注意要与hadoop版本兼容,且选择稳定版较好 wget http://mirrors.hust.edu.cn/apache/hbase/hbase-0.98.5/hbase-0.98.5-hadoop2-bin.tar.gz 2)解压  tar -zxvf hbase-0.98.5-hadoop2-bin.tar.gz 3)修改conf/hbase-site.xml文件  <property>   <name>hbase.rootdir</na

Netty in Action (十七) 第七章节 EventLoop和线程模型

本章节包括: 1)线程模型总览 2)Event Loop概念和具体实现 3)任务调度 4)实现细节 简单地陈述一下,对于一个操作系统,编程语言,框架,或者应用来说,线程模型对其都是至关重要的一部分,在什么时间如何创建一个线程都会对你的代码执行有很重要的影响,所以对于开发人员而言,懂得在各种线程模型里面权衡利弊就是一个很重要的事情,是直接使用线程模型本身还是通过一些框架或者语言提供的线程框架对于开发者而言都是需要选择的 在这个章节,我们将会详细地讲解Netty的线程模型,这个模型是很强大的,且易于

Netty in Action (十二) 第五章节 第一部分 简介ByteBuf

第五章 ByteBuf(分四部分翻译) 本章节包括: 1)ByteBuf------Netty的数据容器 2)API介绍 3)使用案例 4)内存分配 我们之前提到过很多次,网络传输数据的最基本的数据单元是byte,Java的NIO提供了ByteBuffer作为字节的容器,但是这个类的使用有些过于复杂和麻烦 Netty对ByteBuffer提供了一个可选方案ByteBuf,一个很好的解决方案,解决了JDK原生的ByteBuffer的API使用不易的问题,同时ByteBuf为应用程序开发者提供了一系

Netty in Action (二十) 第十章节 codecs

第十章,第十一章序 对于网络而言,数据只是原始字节序列,但是我们的程序将这些字节按照某种方式去组织成我们能够看懂的语言,我们一般称这些信息叫"信息",将信息转换成字节或者从网络中将字节装换成我们能够看懂的信息这些都是我们网络传输中最最常见的任务之一,你可能需要在标准的格式或者协议下工作,例如FTP协议或者Telnet协议,或者是从第三方自定义的专有协议,亦或者是根据字自已的应用去继承一种已有的信息格式 处理将网络中的数据转化成应用数据的组件叫做解码器或者编码器,相对应而言,一个单独的组