(1)堆的特性
1.根节点的值总是大于任何一个子节点的值(大顶堆)
2.每一个节点的左子树和右子树都是一个堆
注意:对左右子树谁大谁小没有要求
(2)建堆
1.有如下的数组,初始情况下的堆为下图
int[] a = {9,12,17,30,50,20,60,65,4,19};
2.因为叶子节点可以视为已经就是堆了,所以根本不用整个调整,只用从第一个非叶子节点向下调整即可(如果根为0,那么第一个非叶子节点下标为n/2-1,n为节点总数)
3.向下调整 对于小顶堆,下标从0开始,则节点i的左孩子下标为2*i+1,右孩子为2*i+2
public void fixDown(int[] a, int parent,int lastIndex)
{
int temp = a[parent];
int child = parent*2+1;
while(child<=lastIndex)
{
if(child+1<=lastIndex && a[child]>a[child+1])
child++;//找出左右孩子中的较小者
if(temp<a[child])//如果调整的节点的值比左右孩子都小,那么说明这就是最终位置
break;
else
{
a[parent] = a[child];
parent = child;
child = parent*2+1;
}
}
a[parent] = temp;//必须不能少
}
4.建堆
int[] a = {9,12,17,30,50,20,60,65,4,19};
for(int i=a.length/2-1; i>=0; i--)
fixDown(a,i,a.length-1);
结果为:
(3)排序
建堆之后,第一次将a[0]与a[n-1]交换,再对a[0...n-2]恢复堆(其实也就是将a[0]向下调整,且最后一个元素就不参与调整了)。第二次将a[0]与a[n-2]交换,再对a[0...n-3]恢复堆(其实也就是将a[0]向下调整,且最后两个元素就不参与调整了)。以此类推,直至a[0]与a[1]交换。因为每次都是将最小的放入后面的有序区间前面,所以操作完成之后整个数组就有序了。
int[] a = {9,12,17,30,50,20,60,65,4,19};
for(int i=a.length/2-1; i>=0; i--)
fixDown(a,i,a.length-1);
for(int i=a.length-1; i>=0; i--)
{
int temp = a[i];
a[i] = a[0];
a[0] = temp;
fixDown(a, 0,i-1);
}
for(int x:a)
System.out.print(x+" ");
结果为:
65 60 50 30 20 19 17 12 9 4
因为建立的是小顶堆,所以排出来的递减序列,如果需要改为正序,那么改为大顶堆即可,其他不用变。
大顶堆的fixDown
public void fixDown(int[] a, int parent,int lastIndex)
{
int temp = a[parent];
int child = parent*2+1;
while(child<=lastIndex)
{
if(child+1<=lastIndex && a[child]<a[child+1])
child++;
if(temp>a[child])
break;
else
{
a[parent] = a[child];
parent = child;
child = parent*2+1;
}
}
a[parent] = temp;
}