从二叉树谈二叉堆:
二叉树可以简单认为是父节点最多由左、右两个子节点组成的树,常用的二叉树有完全二叉树、二叉搜索树。二叉搜索树在极端境况下存在“跛脚”的问题,由此又有了二叉平衡树。二叉堆相比于二叉搜索树条件要宽松一 些,只要求父节点比它的左右孩子都大(小)。
大顶堆、小顶堆
大顶堆的任何一个父节点的值,都大于或等于它左、右孩子 节点的值;
小顶堆的任何一个父节点的值,都小于或等于它左、右孩子 节点的值。
二叉堆的特点:大顶堆的堆顶是整个堆中的最大元素;小顶堆 的堆顶是整个堆中的最小元素。
二叉堆使用数组来储存节点,下面就拿大顶堆来举例讲解二叉堆的操作:
1、上浮,当我们在二叉堆的最后插入一个节点,为了保证二叉堆还是满足最大堆的特点,需要通过不断的上浮来满足条件;
2、下沉,当我们把最大堆的堆顶节点换成另一个节点,这时候需要通过下沉来重新构造大顶堆。
代码实现(java)
下面用java实现大顶堆的上浮和下沉操作
上浮的代码如下:
public void shangfu(int[] array){
// 二叉堆数组的最后一个节点进行上浮
int childIndex = array.length-1;
// 父节点的位置
int parentIndex = (childIndex-1)/2;
int temp = array[childIndex];
// 循环,当子节点大于父节点,上浮
while(childIndex>0&&temp>array[parentIndex]){
array[childIndex] = array[parentIndex];
childIndex = parentIndex;
parentIndex = (parentIndex-1)/2;
}
// 最后将temp进行赋值
array[childIndex]=temp;
}
下沉的代码如下:
public void xiachen(int[] array,int parentIndex,int length){
// 父节点暂时存放
int temp = array[parentIndex];
// 左节点
int childIndex = 2*parentIndex+1;
while(childIndex<length){
// 找出左节点和右节点中的较大节点
if((childIndex<length-1)&&(array[childIndex+1]>array[childIndex])){
childIndex++;
}
// 如果父节点大于子节点
if(temp>=array[childIndex]){
break;
}
// 将子节点赋值给父节点
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2*childIndex+1;
}
// 最后将temp进行赋值
array[parentIndex] = temp;
}
应用:堆排序
下面介绍二叉堆常用的使用场景——堆排序:堆排序的过程是先将数组构建为大顶堆,然后交换堆顶与堆尾的节点,在去除最后一个节点的基础上,通过下沉来重新构建大顶堆,多次循环后,数组就完成由小到大循序排列了。
public void sort(int[] array){
// 构建大顶堆
for(int i=array.length/2-1;i>=0;i--){
xiachen(array,i,array.length);
}
System.out.println(Arrays.toString(array));
for(int i=array.length-1;i>0;i--){
int temp = array[i];
array[i] = array[0];
array[0] = temp;
xiachen(array,0,i);
}
System.out.println(Arrays.toString(array));
}