二叉堆
堆
堆,本质上是一种完全二叉树。分为,最大堆和最小堆。 它的物理存储结构是 数组。
因此:
左孩子节点的 索引值= 它的父节点索引值2+1;
右孩子节点的索引值=它的父节点索引值2+2;
堆的分类
- 最大堆: 任何一个父节点的值都大于或等于它的左右孩子节点的值。
- 最小堆:任何一个父节点的值都小于或等于它的左右孩子节点。
二叉堆的构建
把一个无序的完全二叉树调整为二叉堆。本质是让所有的非叶子节点依次下沉。
/**
* 构建二叉树(最大堆)
* @param datas
*/
public static void buildHeap(int[] datas){
/**
* 最后一个节点的下标
*/
int lastIndex = datas.length - 1;
/**
* 获取最后一个非叶子节点(最后一个节点,可能是左孩子,也可能是有孩子 为什么是-1 而不是 -2)?
*
* 1.如果最后一个节点 是左孩子的话,-1 是无可厚非的,
* 2.如果最后一个节点是有孩子的话,-1 以后,只是余数为1,但是整除的整数位 是正确的,同样可以获取正确的父节点下标。
*/
int lastParentIndex = (lastIndex - 1) / 2;
//依次下沉所有的非叶子节点
for (int parentIndex=lastParentIndex;parentIndex>=0;parentIndex--){
downAdjust(parentIndex,datas);
}
}
/**
* 根节点下沉,构建最大堆
* @param parentIndex 根节点坐标
* @param datas
*/
public static void downAdjust(int parentIndex,int[] datas){
//暂存根节点的值
int temp=datas[parentIndex];
int childIndex=parentIndex*2+1;
while (childIndex<datas.length){
//如果右孩子节点大于左孩子节点时候,比较右孩子节点值
if(childIndex+1<datas.length&&datas[childIndex+1]>datas[childIndex]){
childIndex++;
}
//如果父节点小于子节点的值,子节点的值上浮,父节点值下沉
if(temp<datas[childIndex]){
datas[parentIndex]=datas[childIndex];
//父节点索引变成交换后的子节点(由于,下沉的父节点,最终位置,可能不固定,所以采用单向赋值)
parentIndex=childIndex;
childIndex=parentIndex*2+1;
}else{
//父节点大于等于子节点值,位置正确,停止循环
break;
}
}
//下沉节点最终的位置
datas[parentIndex]=temp;
}
二叉堆的插入
当二叉树插入节点时,插入位置是完全二叉树的最后一个位置,然后通过上浮,重新构造到正确的位置上。
/**
* 二叉堆插入数据
* @param datas
*/
public static void upAdjustBig(int[] datas){
int length=datas.length;
//最后一个子节点
int childIndex=length-1;
int parentIndex=(childIndex-1)/2;
//需要上浮的节点
int temp=datas[childIndex];
while (parentIndex>=0){
//如果子节点大于父节点上浮
if(temp> datas[parentIndex]){
//将父节点移动到子节点的位置
datas[childIndex]=datas[parentIndex];
childIndex=parentIndex;
parentIndex=(childIndex-1)/2;
}else{
break;
}
}
//上浮节点最终的位置
datas[childIndex]=temp;
}
二叉堆删除节点
二叉堆删除节点的过程和插入节点的过程正好相反,所删除的是处于堆顶的节点。
public static void delete(int[] datas){
int lastIndex = datas.length - 1;
//将最后一个元素移替换堆顶元素,最后一个元素为无效的,过期引用。
datas[0]=datas[lastIndex];
downAdjust(0,datas);
}