Java 优先级队列(堆)

二叉树的存储方式

存储方式

使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。
一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。
这种方式的主要用法就是堆的表示。

下标关系

已知双亲(parent)的下标,则:

左孩子(left)下标 = 2 * parent + 1;
右孩子(right)下标 = 2 * parent + 2;

已知孩子(不区分左右)(child)下标,则:

双亲(parent)下标 = (child - 1) / 2;

堆(Heap)

概念

  1. 堆逻辑上是一棵完全二叉树
  2. 堆物理上是保存在数组中
  3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者大堆
  4. 反之,则是小堆,或者小根堆,或者小堆
  5. 堆的基本作用是,快速找集合中的最值

操作-向下调整

前提:左右子树必须已经是一个堆,才能调整。

说明:

  1. array 代表存储堆的数组
  2. size 代表数组中被视为堆数据的个数
  3. index 代表要调整位置的下标
  4. left 代表 index 左孩子下标
  5. right 代表 index 右孩子下标
  6. min 代表 index 的小值孩子的下标

过程(以小堆为例):

  1. index 如果已经是叶子结点,则整个调整过程结束
    1. 判断 index 位置有没有孩子
    2. 因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
    3. 因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界
  2. 确定 left 或 right,谁是 index 的小孩子 min
    1. 如果右孩子不存在,则 min = left
    2. 否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min
  3. 比较 array[index] 的值 和 array[min] 的值,如果 array[index] <= array[min],则满足堆的性质,调整结束
  4. 否则,交换 array[index] 和 array[min] 的值
  5. 然后因为 min 位置的堆的性质可能被破坏,所以把 min 视作 index,向下重复以上过程.

时间复杂度分析:
坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度
即时间复杂度为 O(log(n))

public static void shiftDown(int[] array, int size, int index) {    
	int left = 2 * index + 1;    
	while (left < size) {        
		int min = left;        
		int right = 2 * index + 2;        
		if (right < size) {            
			if (array[right] < array[left]) {                
				min = right;            
			}        
		}                
		if (array[index] <= array[min]) {            
			break;        
		}                
		int t = array[index];        
		array[index] = array[min];        
		array[min] = t; 
		index = min;        
		left = 2 * index + 1;    
	} 
}

操作-建堆

public static void createHeap(int[] array, int size) {    
	for (int i = (size - 1) / 2; i >= 0; i--) {        
		shiftDown(array, size, i);    
	} 
}

堆的应用-优先级队列

概念
在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级高的对象,然后处理 次高的对象。简单的一个例子就是,在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电 话。
在这种情况下,我们的数据结构应该提供两个基本的操作,一个是返回高优先级对象,一个是添加新的对象。 这种数据结构就是优先级队列(Priority Queue)
内部原理
优先级队列的实现方式有很多,但常见的是使用堆来构建。
操作-入队列
过程(以大堆为例):

  1. 首先按尾插方式放入数组
  2. 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
  3. 否则,交换其和双亲位置的值,重新进行 2、3 步骤
  4. 直到根结点
public static void shiftUp(int[] array, int index) {    
	while (index > 0) {        
		int parent = (index - 1) / 2;        
		if (array[parent] >= array[index]) {            
			break;        
		}                
		int t = array[parent];        
		array[parent] = array[index];        
		array[index] = t;                
		index = parent;    
	} 
}

操作-出队列
为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的后一个元素替换堆顶元素,然后通过 向下调整方式重新调整成堆
Java中的优先级队列
PriorityQueue implements Queue

错误处理抛出异常返回特殊值
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peek()

堆的其他应用

1.Top-K问题 面试
2.堆排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值