前言

数据结构队列的学习中,我们知道队列是先进先出的。任务被提交到队列中,按照先进先出的原则

对各个任务进行处理。不过在现实的情况下,任务通常有着优先级的概念,例如短任务、管理员的操作

应该优先执行。这是使用队列就无法描述了,这种特殊的应用我们使用一种称之为优先队列(堆)的方式

来解决。

优先队列

和队列一样优先队列也支持入队和出队操作,不过区别在于优先队列的出队操作出队的是优先级最高

的元素,这里以元素的大小来判定任务的优先级,越小,优先级越高。优先队列的模型如下图:

基于这种优先队列的模型,我们可以多种方法对其进行实现,一种常用的方法就是使用链表以O(1)执

行 插入操作,,遍历最小元素并删除花费O(N)时间。基于优先队列的插入操作一般情况下多于删除操作这

一情况, 前者是一种较好的实现。

另外可以使用二叉查找树来实现,这样一来插入和删除操作都是O(logN),不过二叉树支持多种

操作用以实现优先队列未免有点杀猪用牛刀的感觉。  下面将介绍二叉堆实现优先队列。

二叉堆

二叉堆就结构性质上来说就是一个完全填满的二叉树,满足结构性和堆序性。结构性不必多说,

就是完全二叉树应该满足的树结构。堆序性指的是:父节点的键值总是大于或等于(小于或等于)任何

一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

最大堆:当父节点的键值总是大于或等于任何一个子节点的键值。

最小堆:当父节点的键值总是小于或等于任何一个子节点的键值。

上面图片直接沿用的维基百科上的,笔者懒得在自己做个图了。

结构性质

上面对二叉堆的结构性质略有提及,这里我们进行下详细说明。看看二叉堆是如何

实现的。

仔细观察上述完全二叉树的结构,我们可以发现的是对于任意一个位置i,其

结点的左孩子在2i的位置,右孩子在2i+1的位置上,基于上述的性质我们可以使用

数组来实现二叉堆。

由此二叉堆可以使用一个数组和一个代表当前堆的大小的整数组成。考虑到涉及到

元素大小的比较,该数组元素实现了Comparable接口。

public class BinaryHeap
{/*** Construct the binary heap.*/public BinaryHeap( ){this( DEFAULT_CAPACITY );}/*** Construct the binary heap.* @param capacity the capacity of the binary heap.*/public BinaryHeap( int capacity ){currentSize = 0;array = new Comparable[ capacity + 1 ];}private static final int DEFAULT_CAPACITY = 100;private int currentSize;      // Number of elements in heapprivate Comparable [ ] array; // The heap array
}

以上代码显示的是一个优先队列的架构。至于其提供的具体操作,我们先看看

二叉堆的堆序性。

堆序性

简单的说保证优先队列的删除操作快速执行是堆序性质,基于这个特点我们需要找出最小元素,那么最小元素应该在根结点上,也就是最小堆。这样我们就能以常数时间执行findMin()操作了。

二叉堆基本操作

基于二叉堆的堆序性,我们来看看二叉堆基本操作是如何实现的吧!

insert(插入)

根据优先队列的模型,二叉堆实现的优先队列应该具备插入操作,不过根据其对序性具体的插入操作是如何进行的呢?看下面的演示:

二叉堆插入元素14的情况。

为将一个元素 X 插入到堆中,我们在下一个可用位置创建一个空穴,否则该堆将不是完全数。

如果 X 可以放在该空穴中而不破坏堆的序,那么插入完成。否则,我们把空穴的父节点上的元素

移入该空穴中,这样,空穴就朝着根的方向上冒一步。继续改过程直到 X 能被放入空穴中为止。

这种实现过程称之为上滤:新元素在堆中上滤直到找出正确的插入位置。

实现代码:

public void insert( Comparable x ) throws Overflow{//这里没有进行扩容处理if( isFull( ) )throw new Exception( );// Percolate upint hole = ++currentSize;//上滤,首先找到插入位置,之后元素交换一次for( ; hole > 1 && x.compareTo( array[ hole / 2 ] ) < 0; hole /= 2 )array[ hole ] = array[ hole / 2 ];array[ hole ] = x;}

可以知道的是当插入的元素小于堆中所有的元素的时候,必须上滤到根,插入时间为O(logN)

deleteMin(删除最小元)

基于优先队列的模型,出队的应该是最小元,按照最小堆的堆序性,找出最小元十分容易,麻烦的地方在于删除之后破坏了结构型,这是需要进行一些额外的操作。当删除一个最小元时,要在根节点建立一个空穴。由于现在堆少了一个元素,因此堆中最后一个元素 X 必须移动到该堆的某个地方。如果 X 可以直接被放到空穴中,那么 deleteMin 完成。

不过这一般不太可能,因此我们将空穴的两个儿子中比较小者移入空穴,这样就把空穴向下推了一层。重复该步骤直到 X 可以被放入空穴中。因此,我们的做法是将 X 置入沿着从根开始包含最小儿子的一条路径上的一个正确的位置。

演示过程如下:

首先我们删除根元素13,建立一个空穴,之后判断元素31是否可以放入这个空穴中,明显不能,会破坏堆序性.

之后我们选择较小的左儿子放入空穴,同时空穴下滑一层,之后判断31是否置于空穴中

同上,26置于空穴中,空穴下滑一层,31可以置于空穴中,过程结束。这一种操作过程称之为下滤:空穴一步步下滑.

源码实现:

public Comparable deleteMin( ){if( isEmpty( ) )return null;Comparable minItem = findMin( );array[ 1 ] = array[ currentSize-- ];percolateDown( 1 );return minItem;}
private void percolateDown( int hole ){int child;Comparable tmp = array[ hole ];for( ; hole * 2 <= currentSize; hole = child ){child = hole * 2;if( child != currentSize &&array[ child + 1 ].compareTo( array[ child ] ) < 0 )child++;if( array[ child ].compareTo( tmp ) < 0 )array[ hole ] = array[ child ];elsebreak;}array[ hole ] = tmp;}

同样的这个操作在最坏的情况下为O(logN),平均而言也为O(logN).

优先队列其他操作

上面对二叉堆实现的优先队列的两个基本操作做了一些讲解。我们知道的是在将任务提交给

优先队列的时候有时候我们需要根据实际情况修改任务的优先级。

decreaseKey(降低关键字的值)

desreaseKey(p,m)操作降低在位置p处的值,降值幅度为正m,不过这种方式很可能破坏堆序性,因此需要通过上滤操作进行调整。这种方式能够动态的提高某个任务的优先级,使其在能够优先开始。

increaseKey(降低关键字的值)

与上个操作相反,降低任务的优先级。

delete(删除)

删除堆中某个任务,不过必须先执行decreasekey(P,+ ∞),然后执行deleteMin操作

这种操作的任务并不是正常终止的,而是被用户终止的。

构造二叉堆

上述讲了二叉堆的方式实现优先队列。那么一个二叉堆又是如何构造的呢?

简单的我们可以认为它可以使用N个相继的insert操作来完成。每个insert最坏时间为O(logN)

则其构建时间为O(N)。

更为常用的算法是先保持其结构性,之后再通过检查每个位置,下滤操作使其满足堆序性。

一开始满足结构性,但是并不满足堆序性,我们在元素70的位置进行下滤操作。

代码实现情况如下:

public BinaryHeap( T[] items ){currentSize = items.length;array = (T[]) new Comparable[ (currentSize + 2) * 11 / 10 ];int i=1;for( T item : items ){array[ i++ ] = item;}buildHeap();
}private void buildHeap(){for( int i = currentSize/2; i>0; i-- )percolateDown( i );
}

完整源码:

package com.kiritor;import java.util.Arrays;public class BinaryHeap
{public BinaryHeap( ){this( DEFAULT_CAPACITY );}public BinaryHeap( Comparable[] items ){  currentSize = items.length;  array = new Comparable[ (currentSize + 2) * 11 / 10 ];  int i=1;  for( Comparable item : items ){  array[ i++ ] = item;  }  buildHeap();  }  public BinaryHeap( int capacity ){currentSize = 0;array = new Comparable[ capacity + 1 ];}public void insert( Comparable x ) {// Percolate upint hole = ++currentSize;for( ; hole > 1 && x.compareTo( array[ hole / 2 ] ) < 0; hole /= 2 )array[ hole ] = array[ hole / 2 ];array[ hole ] = x;}public Comparable findMin( ){if( isEmpty( ) )return null;return array[ 1 ];}public Comparable deleteMin( ){if( isEmpty( ) )return null;Comparable minItem = findMin( );array[ 1 ] = array[ currentSize-- ];percolateDown( 1 );return minItem;}private void buildHeap( ){for( int i = currentSize / 2; i > 0; i-- )percolateDown( i );}public boolean isEmpty( ){return currentSize == 0;}public boolean isFull( ){return currentSize == array.length - 1;}public void makeEmpty( ){currentSize = 0;}private static final int DEFAULT_CAPACITY = 100;private int currentSize;      // Number of elements in heapprivate Comparable [ ] array; // The heap arrayprivate void percolateDown( int hole ){int child;Comparable tmp = array[ hole ];for( ; hole * 2 <= currentSize; hole = child ){child = hole * 2;if( child != currentSize &&array[ child + 1 ].compareTo( array[ child ] ) < 0 )child++;if( array[ child ].compareTo( tmp ) < 0 )array[ hole ] = array[ child ];elsebreak;}array[ hole ] = tmp;}// Test programpublic static void main( String [ ] args ){int numItems = 50;BinaryHeap h = new BinaryHeap( numItems );int i = 37;try{for( i = 37; i != 0; i = ( i + 37 ) % numItems )h.insert( new Integer( i ) );System.out.println(Arrays.toString(h.array));System.out.println(h.findMin());h.deleteMin();System.out.println(Arrays.toString(h.array));}catch( Exception e ){ System.out.println( "Overflow (expected)! " + i  ); }}
}

简单执行结果:

[null, 1, 2, 7, 6, 3, 12, 10, 9, 8, 5, 4, 13, 24, 22, 11, 34, 21, 19, 16, 27, 17, 15, 14, 25, 38, 31, 49, 36, 23, 18, 47, 48, 35, 42, 45, 46, 32, 29, 43, 40, 30, 37, 41, 33, 28, 20, 39, 44, 26, null]
1
[null, 2, 3, 7, 6, 4, 12, 10, 9, 8, 5, 14, 13, 24, 22, 11, 34, 21, 19, 16, 27, 17, 15, 20, 25, 38, 31, 49, 36, 23, 18, 47, 48, 35, 42, 45, 46, 32, 29, 43, 40, 30, 37, 41, 33, 28, 26, 39, 44, 26, null]

                                                                                                                             By      Kiritor

                                                                                                                             2013 /06 /16   父亲节

转载于:https://blog.51cto.com/kiritor/1226661

数据结构之优先队列--二叉堆(Java实现)相关推荐

  1. java实现二叉堆,数据结构基础篇-二叉堆

    二叉堆分为两种,最大堆和最小堆,我们只讨论最小堆的性质,最大堆具有相同的原理. 最小堆是一种符合下面两个特性的树形结构: 最小堆是一颗完全二叉树,即最小堆的每个节点要么没有子节点,要么只有一个左子节点 ...

  2. 数据结构与算法--二叉堆(最大堆,最小堆)实现及原理

    二叉堆(最大堆,最小堆)实现及原理 二叉堆与二叉查找树一样,堆也有两个性质,即结构性质和堆性质.和AVL树一样,对堆的一次操作必须到堆的所有性质都被满足才能终止,也就是我们每次对堆的操作都必须对堆中的 ...

  3. 优先队列-二叉堆-堆排序原理-Java相关API

    完全二叉树概念 除了最后一层,前面所有层都是满的 最后一层是从左到右 是一个二叉树 堆 满足完全二叉树 父节点存储的元素比子节点大 上浮 不符合堆规则的节点,与父节点交换 直到上浮到符合为止 下沉 不 ...

  4. 【数据结构与算法拓展】二叉堆原理、实现与例题(C和java)

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  5. 【数据结构与算法】二叉堆V2.0的Java实现

    更新说明 我们在此前已经编写过简单版的二叉大根堆V1.0,这次,换成二叉小根堆,命名为二叉堆V2.0. 大家也知道,堆是完全二叉树,存储方式借助动态数组实现顺序存储,依赖于父子结点之间的index关系 ...

  6. java优先队列二叉_二叉堆与Java中的优先队列

    之前在A*算法演示程序的编码过程中,发现javaScript并没有原生的优先队列,于是去Java中找到了PriorityQueue类,研究了一下源码.Java中的优先队列基于最小二叉堆实现.最小二叉堆 ...

  7. 排序算法之——优先队列经典实现(基于二叉堆)

    许多应用都需要处理有序的元素,但有时,我们不要求所有元素都有序,或是一定要一次就将它们排序,许多情况下,我们会收集这些元素里的最大值或最小值. 这种情况下一个合适的数据结构应该支持两种操作:插入元素. ...

  8. 《恋上数据结构第1季》二叉堆实现优先级队列

    优先级队列(Priority Queue) 优先级队列简介 优先队列的底层实现 二叉堆实现优先级队列源码 测试代码 数据结构与算法笔记目录:<恋上数据结构> 笔记目录 想加深 Java 基 ...

  9. 优先队列与相关题目(Python、二叉堆)

    1. 优先队列知识 1.1 优先队列简介 优先队列:一种特殊的队列.在优先队列中,元素被赋予优先级,当访问队列元素时,具有最高优先级的元素最先删除. 优先队列与普通队列最大的不同点在于出队顺序 普通队 ...

最新文章

  1. 大专生质问马云:你说招聘从不看文凭,为什么我投阿里石沉大海
  2. 找出两列数据的差集_excel快速查找数据差异项
  3. 机器学习:朴素贝叶斯分类器代码实现,决策函数非向量化方式
  4. Linked List Cycle
  5. python中pass的使用_Python中pass语句的作用
  6. 通过管理门户部署云服务与VIP交换
  7. freemarker 解析对象的某元素_Freemarker常用技巧(三)
  8. 筛选DataTable数据的方法
  9. 基于xxx的系统实现
  10. 基于人机环境系统工程的智慧企业建设思考(2)
  11. SPSS Modeler 和spss statistics有什么不同
  12. Poker Ⅱ 机械键盘使用说明书 自备
  13. 手写一个多行文本溢出省略号显示,多行文本溢出省略号显示,单行文本溢出省略号显示
  14. Excel怎么隐藏指定文本单元格整行
  15. 无线ap的ntp服务器,怎么配置cisco路由器的NTP
  16. 自定义微信小程序顶部导航栏(自适应微信胶囊按钮,flex布局)
  17. 阿里云ARM服务器通用型g6r实例CPU性能参数详解
  18. git项目拉下来之后无法找到主加载类
  19. 查看局域网内其它电脑名称和IP
  20. 为什么我们的代码难以维护(草稿)

热门文章

  1. Flutter中的提示工具
  2. flutter - dart基础之map
  3. 神策 FM | 20 种方法,让你成为用户的知心朋友
  4. Shell命令-文件及目录操作之chattr、lsattr
  5. Linux文件系统和挂载点理解
  6. C++的坑真的多吗?
  7. Android零基础入门第20节:CheckBox和RadioButton使用大全
  8. 在eclipse中把项目部署到tomcat中时,发现项目文件不完整
  9. 如何通过插件携带第二个单据体到下游单据
  10. [数据结构]快速排序