博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
堆 (heap)
阅读量:2342 次
发布时间:2019-05-10

本文共 2231 字,大约阅读时间需要 7 分钟。

转自

堆(heap)又被为优先队列(priority queue)。尽管名为优先队列,但堆并不是队列。回忆一下,在中,我们可以进行的限定操作是dequeue和enqueue。dequeue是按照进入队列的先后顺序来取出元素。而在堆中,我们不是按照元素进入队列的先后顺序取出元素的,而是按照元素的优先级取出元素。

这就好像候机的时候,无论谁先到达候机厅,总是头等舱的乘客先登机,然后是商务舱的乘客,最后是经济舱的乘客。每个乘客都有头等舱、商务舱、经济舱三种个键值(key)中的一个。头等舱->商务舱->经济舱依次享有从高到低的优先级。

再比如,封建社会的等级制度,也是一个堆。在这个堆中,国王、贵族、骑士和农民是从高到低的优先级。

封建等级

 

Linux内核中的调度器(scheduler)会按照各个进程的优先级来安排CPU执行哪一个进程。计算机中通常有多个进程,每个进程有不同的优先级(该优先级的计算会综合多个因素,比如进程所需要耗费的时间,进程已经等待的时间,用户的优先级,用户设定的进程优先程度等等)。内核会找到优先级最高的进程,并执行。如果有优先级更高的进程被提交,那么调度器会转而安排该进程运行。优先级比较低的进程则会等待。“堆”是实现调度器的理想数据结构。

(Linux中可以使用nice命令来影响进程的优先级)

 

堆的实现

堆的一个经典的实现是完全二叉树(complete binary tree)。这样实现的堆成为二叉堆(binary heap)

完全二叉树是增加了限定条件的二叉树。假设一个二叉树的深度为n。为了满足完全二叉树的要求,该二叉树的前n-1层必须填满,第n层也必须按照从左到右的顺序被填满,比如下图:

为了实现堆的操作,我们额外增加一个要求: 任意节点的优先级不小于它的子节点。如果在上图中,设定小的元素值享有高的优先级,那么上图就符合该要求。

这类似于“叠罗汉”。叠罗汉最重要的一点,就是让体重大的参与者站在最下面,让体重小的参与者站在上面 (体重小,优先级高)。为了让“堆”稳固,我们每次只允许最上面的参与者退出堆。也就是,每次取出的优先级最高的元素。

三个“叠罗汉”堆

 

我已经在中实际使用了堆。堆的主要操作是插入删除最小元素(元素值本身为优先级键值,小元素享有高优先级)。在插入或者删除操作之后,我们必须保持该实现应有的性质: 1. 完全二叉树 2. 每个节点值都小于或等于它的子节点。

 

插入操作的时候,会破坏上述堆的性质,所以需要进行名为percolate_up的操作,以进行恢复。新插入的节点new放在完全二叉树最后的位置,再和父节点比较。如果new节点比父节点小,那么交换两者。交换之后,继续和新的父节点比较…… 直到new节点不比父节点小,或者new节点成为根节点。这样得到的树,就恢复了堆的性质。

我们插入节点2:

插入

 

删除操作只能删除根节点。根节点删除后,我们会有两个子树,我们需要基于它们重构堆。进行percolate_down的操作: 让最后一个节点last成为新的节点,从而构成一个新的二叉树。再将last节点不断的和子节点比较。如果last节点比两个子节点中小的那一个大,则和该子节点交换。直到last节点不大于任一子节点都小,或者last节点成为叶节点。

删除根节点1。如图:

删除根节点

 

代码实现:
public class HeapSort {	/**	 * @param args	 */		public static void heapSort(int[] array){		buildHeap(array); 		int n = array.length;		int i = 0;		for(i = n - 1; i >= 1;i--){ 			swap(array, 0 ,i);			sink(array, 0, i);		}	}		public static void buildHeap(int[] array){		int n = array.length;		for(int i = n/2 - 1; i >= 0; i--)		{			sink(array, i , n);		}	}		public static void sink(int[] array, int node, int n){		int left = 2 * node + 1;		int right = 2 * node + 2;		int largest = 0;		if(left < n && array[left] > array[node]){			largest = left;		}		else{			largest = node;		}				if(right < n && array[right] > array[largest]){			largest = right;		}				if(largest != node){			swap(array, largest, node);			sink(array, largest, n);		}	}		public static void swap(int[] array, int i, int j){		int temp = array[i];		array[i] = array[j];		array[j] = temp;	}}

转载地址:http://vmbvb.baihongyu.com/

你可能感兴趣的文章
ubuntu12.04--无法获得锁 /var/lib/dpkg/lock - open (1...
查看>>
ubuntu12.04--子进程 已安装 post-installation 脚本 返回了错误号 1
查看>>
系统--电脑开机一声长响
查看>>
系统--A disk read error occurred Press Ctrl+Alt+d...
查看>>
系统--把系统BIOS中将光驱设置为第一启动盘
查看>>
Some projects cannot be imported because they a...
查看>>
ubuntu-android--make: *** [out/host/linux-x86/o...
查看>>
原子变量与synchronized详细解释
查看>>
activiti--多实例任务实现会签
查看>>
ie和firefox操作table对象的异同
查看>>
Hadoop 实现定制的Writable类型(附部分源码)
查看>>
一台机器上运行多个ActiveMq
查看>>
java.lang.OutOfMemoryError: PermGen space及其解决方法
查看>>
Nginx配置文件nginx.conf中文详解
查看>>
如何让ajaxfileupload.js支持IE9,IE10,并可以传递多个参数?
查看>>
highcharts扩展tooltip提示异步信息
查看>>
activiti--History 历史配置
查看>>
并行计算框架 Apache Hama
查看>>
eclipse中tomcat启动超时解决办法
查看>>
异质链表
查看>>