概念
最大(小)堆: 完全二叉树且所有的非叶子节点大于(等于)等于其子节点,即每一个子节点都为最大(小)堆
属性:最大(小)堆的最大(小)的值为根结点。
操作:
- push:添加一个结点,仍然是最大(小)堆。
- pop :移除根节点(也就是最大或者最小值),仍然是最大(小)堆。
- build:将一棵完全二叉树构建成一个最大(小)堆。
分析-构建一个最大堆
可以通过数组或者链表作为基本的数据结构(本文选取数组)。根结点index为0;
父节点=(子节点-1)/2;
左子节点=父节点×2+1;
右子节点=父节点×2+2;
- push:
①将结点放入堆最末尾,作为最后一个叶结点。
②将该节点p与其父节点parent比较,若父节点大于等于子节点,则结束;若子节点大于父节点,则交换两个结点。
③交换之后,将parent赋值给p,重复操作②,直到p变成根结点或者在②中满足第一个条件退出—迭代
//递归比较,比较新增的结点和其父节点的大小
void push(Integer node){
if(index+1>=size){
return;//堆已满
}
maxHeap[++index]=node;//index作为成员变量,表示最后一个叶节点的位置
int p=index;
while(p!=0){//0是根节点
int parent=(p-1)/2;//父节点的index
if(maxHeap[parent].compareTo(maxHeap[p])>=0){//若父节点大于子节点,说明树是一个最大堆。
break;
}else {//否则,交换两个结点,继续与父节点的父节点比较
swap(parent,p);
p=parent;
}
}
}
- pop:
①将最后一个叶节点移到根节点,记录最后一个结点位置的index减1。表示已经移出了根结点。当前堆不一定为最大堆,需调整。
②将根节点root与其左右子节点(left,right)比较,若根节点为最大值,则不变;否则将最大的子节点与更结点交换位置。
③交换位置之后,被交换位置的子节点不一定是最大堆,需调整。将该结点赋值给root,重复操作②,直到②中第一个条件满足或者子节点不存在(超出index);
Integer pop(){
if(index<0)//堆为空
return null;
Integer result=maxHeap[0];//返回的根节点
maxHeap[0]=maxHeap[index--];//将最后一个叶节点移到根结点
int rootIndex=0;
while (rootIndex<=index){//如果结点不存在,则返回
int leftIndex=rootIndex*2+1;//左节点索引
int rightIndex=rootIndex*2+2;//右结点索引
Integer left=null;
Integer right=null;
Integer root=maxHeap[rootIndex];
if(leftIndex<=index){
left=maxHeap[leftIndex];
}
if(rightIndex<=index){
right=maxHeap[rightIndex];
}
if(left!=null&&right!=null){//如果左右结点存在
if(left.compareTo(right)>0&&left.compareTo(root)>0){//如果左结点最大,交换根结点和左结点
swap(leftIndex,rootIndex);
rootIndex=leftIndex;
}else if(right.compareTo(left)>0&&right.compareTo(root)>0) {//如果右结点最大,交换根结点和右结点
swap(rightIndex,rootIndex);
rootIndex=rightIndex;
}else { //否则不变
break;
}
}else if(left!=null){//如果仅有左节点存在(因为是完全树,所以不存在仅右结点存在的情况)
if(left.compareTo(rootIndex)>0){
swap(leftIndex,rootIndex);
rootIndex=leftIndex;
}else {
break;
}
}else{//其它情况,左右结点均为空结点
break;
}
}
return result;
}
- build:构建有两种方法,一种是通过push操作,一个一个添加即可。