日升时奋斗,日落时自省
目录
一、优先级队列
1、概念
队列是一种先进先出(FIFO)的数据结构,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列
数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象,这就是优先级队列
2、优先级队列得模拟实现
在priorityQueue底层使用了堆的数据结构
2.1堆的概念图解
堆就是将完全二叉树装入一个一维数组中,但是对于节点大小是有要求的,在该完全二叉树中每个根节点大于子节点的就是大根堆,另一种,每个根节点都小于子节点的就是小根堆
堆的性质:
(1)总是一个完全二叉树
(2)堆中某个节点的值总是不大于或不小于其父节点的值
图解:小根堆
图解:大根堆
2.2堆的存储方式
将树从根节点按层序进入数组(图解)
注意:堆一定是完全二叉树,因此这里采用是层序的规则采用顺序的方式来高效存储,而如果是非完全二叉树,则不适合使用顺序方式进行存储,为了能够还原二叉树,空间中就会存储空节点,导致空间利用率降低
这里只是图解一个堆中是怎样存储数据的,在实现代码之前我们还需要知道以下几个点:
这里首先设i为节点在数组中的下标:
如果i为0表示根节点,则i节点的双亲节点为(i-1)/2
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子
3、堆的创建
3.1创建一个大根堆 (创建)
小根堆与其相反,以下代码可以稍作调整便可以实现
注释:c表示(child)左孩子节点,p表示(parents)父节点
如何将一个层序规则完全二叉树的数组改为一个大根堆,这里需要知道最后一个节点当前的父节点满足大根堆,依次倒回去 (图解)
当前可以建立一个数组,为我们初期学习的使用
实现大根堆每次需要的交换
public void creatHeap(){
for (int parents = (this.usedSize-1-1)/2; parents >=0 ; parents--) { //当前就是每次父节点向前移动
shiftDown(parents,this.usedSize); //向下交换
}
}
public void shiftDown(int parents,int len){
int child=2*parents+1;
while(child<len){ //防止数组越界 孩子节点也是要在有效范围内的
if(child+1<len&&this.elem[child]<this.elem[child+1]){ //避免没有右孩子,同时也让左孩子永远都是最大的
child++;
}
if(this.elem[child]>this.elem[parents]){
int tmp=this.elem[child];
this.elem[child]=this.elem[parents];
this.elem[parents]=tmp; //这里是为了把交大的节点给父节点
parents=child; //向下,为了保证当前树下的都是大根堆
child=parents*2+1; //孩子节点也向下
}else {
break; //如果该节点已经是大根堆了就结束当前循环
}
}
}
运行结果:
3.2在大根堆中插入(插入)
插入:插入值首先放入数组的最后一个位置
插入条件:(针对写代码)
(1)要判断是不是满了
(2)跳出条件是c(child)==0时就算走完了,所以跳出条件c>0
public void offer(int val){
if(isFull()){
this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
}
this.elem[usedSize]=val;
usedSize++;
shiftUp(this.usedSize-1); //因为前面加一,所以这里我们要下标为10的数据就需要-1
}
public void shiftUp(int child){
int parents=(child-1)/2;
while (child>0){
if(this.elem[child]>this.elem[parents]){ //大于的情况下
int tmp=this.elem[child]; //向上交换
this.elem[child]=this.elem[parents];
this.elem[parents]=tmp;
child=parents; //向上走
parents=(child-1)/2;
}else{
break;
}
}
}
public boolean isFull(){ //判断满了没有
return this.usedSize==elem.length;
}
3.3堆中删除(删除)
删除堆定元素:直接删除会导致这个树大幅度的移动
思路:
(1)将堆顶与最后一个节点换位置,实现一条路被受影响
(2)从上向下只需要根节点跑一次树的高度就可以
public int poll(){
if(isEmpty()){ //为空可以抛出一个异常
throw new NullPointerException();
}
int ret=this.elem[0];
this.elem[0]=this.elem[usedSize-1];
this.elem[usedSize-1]=ret; //将堆顶与最后一个数据交换位置
usedSize--;
shiftDown(0,this.usedSize); //调用一次向下交换
return ret;
}
public boolean isEmpty(){ //判断是否为空
return usedSize==0;
}
3.4查看堆顶(探顶)
public int peek(){
if(isEmpty()){ //为空可以抛出一个异常
throw new NullPointerException();
}
return this.elem[0];
}