注意:此案例演示的是最大堆
先来看一下书上对堆的介绍,嫌烦的可以直接往下翻==
堆是一个完全二叉树,这里演示的是最大堆,最大堆特点是父节点要大于任何一个子节点
堆只是概念上的表示,实际数据还是存在数组里
然后来演示一下如何在堆中插入数据、
1:要在如下的堆中插入数据99
2:插入时并不是直接插在根节点上,(虽然99在当前堆中是最大的 ),而是插在最后(在数组中表示的话也是最后),(注意:堆是完全二叉树),没明白的百度一下完全二叉树,你就知道为什么要这样插了
3:插好之后,可不能往后一插就了事;
按照最大堆的规则:父节点要比任一子节点大,还要向上作比较以确定合适的位置
那么看图:因为99比80大,所以两两互换,99在上,80在下
4:然后在进行比较,99比83大,两两互换,99在上,83在下
5:然后发现99还是比92大,所以再换,99成根节点了,92下去了
(注意:因为这里插入的数是最大的所以一直交换到根,如果插入不是最大数,在比较过程中发现父节点比插入数大,那么插入数就待原地不动,插入方法结束)
插入讲完了
下面来讲一下删除
在最大堆中,删除始终删除最大值,也就是其根节点,看下图,最大值为97,我们要删除他
删除后,根节点总不能空吧,空了那还是树?所以我们要找一个节点提上去,因为他是完全二叉树,我们不能随便对里面节点操作,所以我们将最后一节点 33 先提上来 ,提上来依旧符合完全二叉树的性质
33提上来了,这时发现33并不是最大的,放在根节点又不太合适啊,所以还要在做调整,将最大的放到根上来,
33此时为根,从其左右子节点找最大的提上来,然后95上来了,33跑到95原来的地方
然后33还是比子节点小,然后在从其左右子节点中找最大的换上来,57最大,所以33和57兑换
然后33还是小,再换
最后,33比0大,所以不换了
package heap;
public class Node
{
public int data ;
public Node ( int data )
{
this.data = data ;
}
}
package heap;
/*
* 堆在概念上是用完全二叉树,实际上是用数组保存数据
*
* PS:把上面几个操作的流程图看明白再来看代码,理解的更快
*/
public class MaxHeap
{
private Node [] heapArray ; //堆的实体是一个数组
private int currentSize ;//数组当前大小
private int maxSize ;//数组初始化最大长度
//初始化
public MaxHeap ( int maxSize )
{
this.maxSize = maxSize ;
currentSize = -1 ;
heapArray = new Node [maxSize] ;
}
//判断是否为空
public boolean isEmpty ()
{
return currentSize == - 1 ;
}
//判断是否满
public boolean isFully ()
{
return currentSize == maxSize - 1 ;
}
//添加数据
public boolean insert ( int data )
{
//先判断满没满
if ( isFully () )
{
System.out.println ( "Heap is full !" );
return false ;
}
//没满,添加数据
Node newNode = new Node ( data ) ;
//添加新数据,在数组中表示新数据一定是在数组末端
//在树中,是最下层最后一个节点(叶节点)
heapArray [ ++currentSize ] = newNode ;
//但又不能添加后就了事啊,万一新数据比父节点大呢,所以还要在向上调整
trickleUP ( currentSize ) ;
return true ;
}
//向上调整
public void trickleUP ( int index )
{
//由于堆的具体实现是用数组来实现的,想要确定当前节点的父节点,要比在树中要难
//那么问题来了,如何在数组中确定当前新数据所对应节点的父节点下标?=_=
// 有一个公式:(当前节点的数组索引下标 - 1 )/ 2
int parent = ( index - 1 ) / 2 ;//当前节点的父节点数组索引下标
Node current = heapArray [ index ] ;//存放要调整的节点
//父节点比当前节点小
while ( index > 0 && heapArray [parent].data < current.data )
{
//将父节点移动当前节点的位置
heapArray [index] = heapArray [parent] ;
index = parent ;//当前节点下标变成原父节点下标
parent = (parent-1) / 2 ;//继续比较
}
//跳出循环说明要么当前节点到根哪儿了,要么其父节点比他大
heapArray [index] = current ;
}
//删除
public Node remove ()
{
//删除始终删除其根节点,也就是数组下标为0的
Node root = heapArray [0] ;
//删除完了,将最后节点提上来
heapArray [0] = heapArray [currentSize] ;
currentSize -- ;//原根节点被删了,最后节点又提上去了,所以数组当前长度-1
//然后向下调整
trickleDOWN ( 0 ) ;
return root ;
}
//向下调整
public void trickleDOWN ( int index )
{
//寻找其此时所对应树结构的左右孩子
//如何确定左右孩子在数组中的下标
// left : (index+1)*2-1
//right: (index+1)*2
int left = ( index + 1 ) * 2 - 1 ;
int right = ( index + 1 ) * 2 ;
if ( left > currentSize )
{
return ;
}
Node current = heapArray [index] ;
//如果左子节点比右子节点大
if ( heapArray [left].data > heapArray [right].data )
{
//如果左子节点比当前节点大
if ( heapArray [index].data < heapArray [left].data )
{
heapArray [index] = heapArray [left] ;
index = left ;
trickleDOWN ( index ) ;
}
//如果左子节点比当前节点小
else
{
return ;
}
}
else//如果左子节点比右子节点小
{
如果右子节点比当前节点大
if ( heapArray [index].data < heapArray [right].data )
{
heapArray [index] = heapArray [right] ;
index = right ;
trickleDOWN ( index ) ;
}
else//如果右子节点比当前节点小
{
return ;
}
}
heapArray [index] = current ;
return ;
}
}
图片来源于互联网