优先级队列(小堆/大堆)

1.优先级队列

(1)概念
普通队列有着先进先出的性质

比如:入队顺序:3,1,2
      出队顺序:3,1,2

但优先级队列不同

比如:入队顺序:3,1,2
      出队顺序:1,2,3(此种为队首为最小元素的优先级队列)
            或3,2,1(此种为队首为最大元素的优先级队列)

因为优先级队列有着这样的特性,所以它存放的元素必须是可以比较大小的。

(2)优先级队列的特性:

  1. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
  2. ClassCastException异常
  3. 不能插入null对象,否则会抛出NullPointerException
  4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
  5. 插入和删除元素的时间复杂度为
  6. PriorityQueue底层使用了堆数据结构

(3)优先级队列的构造方法
这里只列举常见的几种,详细的请查询jdk-api。


    /*无参构造方法:底层默认容量为11*/
	ProirityQueue<Integer> queue = new PriorityQueue<>();
	
	/*创建一个空的优先级队列,底层的容量为initialCapacity*/
	PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
	
	// 用ArrayList对象来构造一个优先级队列的对象
	ArrayList<Integer> list = new ArrayList<>();
	list.add(4);
	list.add(3);
	list.add(2);
	list.add(1);	
	PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
			

(4)优先级队列的常见操作方法
在这里插入图片描述

2.底层实现----堆

将优先队列里存储的N个元素看成集合K={K0,K1,K2,…,Kn-1}按照完全二叉树的顺序存储方式存储在一维数组中:
如果:

  • Ki <= K2i+1 且 Ki<= K2i+2,称这样的集合为小堆
  • Ki >= K2i+1 且 Ki>= K2i+2,称这样的集合为大堆

(1)堆的性质: 堆中的某个结点的值总是不大于(大堆)或不小于(小堆)其父节点的值。

(2)堆的存储方式
堆是一棵完全二叉树,可以按照其层序遍历的顺序存储在一维数组。
在这里插入图片描述
对于存储在数组中的完全二叉树,它的特点可以利用数组下标表示:(i为数组下标编号,N为节点总数)

  • 若i=0,i所对应的元素为根结点,它没有双亲结点;若i!=0,则它的双亲结点下标为(i-1)/2;
  • 若2i+1<N,则此节点左孩子下标为2i+1,否则它就没有左孩子;
  • 若2i+2<N,则此节点左孩子下标为2i+2,否则它就没有右孩子;

3.堆的创建

(1)向下调整

在这里插入图片描述
对于这样一个完全二叉树,如何将它变成堆(此处实现的是小堆)。

向下调整

向下调整适用于根结点的左右子树已经满足堆的性质的情况
在这里插入图片描述
例如这个堆,4的左子树和右子树均已满足小堆的性质,直接使用一次向下调整就可以将整个堆变成小堆。
向下调整实现:
记录根结点为parent,孩子结点为child

void shiftDown(int[] array,int parent){
	int child = parent*2+1;
	//child总是标记最大的孩子,此处先让他标记左孩子。因为一个结点不一定有右孩子。
	
	while(child < size ){
		//有孩子比左孩子大,child标记右孩子
		if(child+1 < size && array[child+1] > array[child]){ 
			child = child + 1;
		}
		if(array[parent] <= array[child]){
			break;//满足小堆
		}else{
		    swap(int[] array, int parent, int child);//交换值
			parent = child;
			child = parent*2 + 1;//让child再次标记parent的左孩子
		}
	}
}

构造堆:
从底层构造堆,反复调用向下调整:

int[] array={5,3,0,6,2,8,4,7,1,6,9}
//jdk-api中没有此种构造方法,这里只是样例
public MyPriorityQueue(int array[]){
	int root = (array.length-2)>>1;//找到最后一个非叶子节点
	for(; root >= 0; root--){
		shiftDown(array,root);
	}
}

从小堆中删除堆顶元素:
将堆中最后一个元素与堆顶元素交换,array.length-1,然后再做一次向下调整。

向上调整

向小堆中添加元素:
向堆中添加元素,要从末尾添加。此时已打破了小堆的性质,这时,也不能使用向下调整,此时,根结点的左右子树已经不满足小堆的性质。

void shiftUp(int[] array,int child){
     int parent = (child-1)>>1;
     while(parent >= 0){
     	if(array[parent] <= array[child]){
     		break;//满足小堆性质
     	}else{
     		swap(array,child,parent);
     		child = parent;
     		parent = (child-1)>>1;
     	}
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值