堆的定义:
1、堆是完全二叉树,能快速的访问的树中最大或者最小的元素。
2、他常常使用一个数组实现。
3、堆中每个节点的父节点的值,都大于或者小于这个节点的值。当堆中的父节点总是大于子节点的时候,我们称之为最大堆,反之称之为最小堆。
如下图a和b所示,他是一个最小堆与最大堆(图片摘自网络http://www.lxway.com/225509804.htm,侵权请告知)
堆的索引:
堆用数组表示的话,若数组中节点的索引为i,那么
1、他的父节点的下标为(i-1)/2;
2、他的左子节点的下标为2*i+1;
3、他的右子节点的下标为左子节点的下标+1;
堆的操作----插入与删除:
堆的插入(以最小堆为例):
1、保证堆没满的前提下,把节点插入数组末尾。
2、向上筛选插入的节点,这个筛选的过程其实是节点复制的过程,直到他的父节点比他小,或者没有到根。
3、堆容量加1.
//插入节点
public boolean insert(Node data){
if(currentSize==maxSize){
return false;
}
Node node=data;
heaparr[currentSize]=node;
trickleUp(currentSize++);
return true;
}
//向上筛选
public void trickleUp(int index){
int parent=(index-1)>>1;
Node bottom=heaparr[index];
while(index>0 && bottom.data<heaparr[parent].data){
heaparr[index]=heaparr[parent];
index=parent;
parent=(parent-1)>>1;
}
heaparr[index]=bottom;
}
堆的删除(以最小堆为例):
堆的删除总是移去根节点,所以按照以下步骤
1、移走根。
2、将末尾的节点移动到根节点处
3、如果移除节点的父节点比他大,那么我们一直向下调节,对比移除节点的左子树和右子树,获得其中最小的节点,用最小节点和移除节点对比,如果最小节点比移除节点小,则交换节点,否则,就说明他已经在合适的位置上了。
4、堆容量减去1
//删除节点
public static Node remove(){
Node root=heaparr[0];
heaparr[0]=heaparr[currentSize--];
trickleDown(0);
return root;
}
//向下调节
public static void trickleDown(int index){
Node top=heaparr[index];
int largChild=0;
//是否到达最后一层
while(index<currentSize/2){
int leftChild=(index<<1)+1;
int rightChild=leftChild+1;
if(leftChild<currentSize && heaparr[leftChild].data<heaparr[rightChild].data){
largChild=leftChild;
}else if(rightChild<currentSize && heaparr[leftChild].data>heaparr[rightChild].data){
largChild=rightChild;
}
if(top.data>heaparr[largChild].data){
heaparr[index]=heaparr[largChild];
index=largChild;
}else{
break;
}
}
heaparr[index]=top;
}
现在给出堆(最小堆)的完整代码:
public class Heap {
private Node[] heaparr; //用来存取节点的堆数组
private int maxSize; //最大容量
private int currentSize; //堆的末尾指针
public Heap(int maxSize){
this.maxSize=maxSize;
heaparr=new Node[maxSize];
currentSize=0;
}
//插入节点
public boolean insert(int data){
if(currentSize==maxSize){
return false;
}
Node node=new Node(data);
heaparr[currentSize]=node;
trickleUp(currentSize++);
return true;
}
//向上筛选
public void trickleUp(int index){
int parent=(index-1)>>1;
Node bottom=heaparr[index];
while(index>0 && bottom.data<heaparr[parent].data){
heaparr[index]=heaparr[parent];
index=parent;
parent=(parent-1)>>1;
}
heaparr[index]=bottom;
}
//删除节点
public Node remove(){
Node root=heaparr[0];
heaparr[0]=heaparr[currentSize--];
trickleDown(0);
return root;
}
//向下调节
public void trickleDown(int index){
Node top=heaparr[index];
int largChild=0;
//是否到达最后一层
while(index<currentSize/2){
int leftChild=(index<<1)+1;
int rightChild=leftChild+1;
if(leftChild<currentSize && heaparr[leftChild].data<heaparr[rightChild].data){
largChild=leftChild;
}else if(rightChild<currentSize && heaparr[leftChild].data>heaparr[rightChild].data){
largChild=rightChild;
}
if(top.data>heaparr[largChild].data){
heaparr[index]=heaparr[largChild];
index=largChild;
}else{
break;
}
}
heaparr[index]=top;
}
public int getHeapTop(){
if(currentSize<0){
return -1;
}
return heaparr[0].data;
}
//节点结构
private class Node{
public int data;
public Node(int data){
this.data=data;
}
}
}
堆的排序:
我们都知道根节点是最大或者最小的节点,我们普通的删除插入就可以让一组无序的数组变的有序,这里给出O(N*logN)的代码。
//堆排序
public void MinheapsortTodescendarray(Node[] arr){
//边界查询
if(arr==null || arr.length==0){
return;
}
for(int i=0;i<arr.length;i++){
insert(arr[i]);
}
for(int i=0;i<arr.length;i++){
arr[i]=remove();
}
}
参考资料:
Java数据结构与算法:第12章 堆
白话经典算法系列之堆与堆排序:http://blog.csdn.net/morewindows/article/details/6709644/
百度百科: 堆与堆排序