优先队列之二叉堆
什么是优先队列呢?优先队列顾名思义它是一种队列,但是是优先的那种,那么他优先在哪了?
他像我们普通的queue一样一个地方输入,在另一端输出,但是他的输出却是输出最小的。
也就是说,优先队列的工作原理是删除并返回优先队列中最小的元素。
如图
二叉堆
什么是二叉堆呢?首先我们需要知道优先队列他又称之为堆,二叉堆也就是像一个被完全填满的二叉树,也就是我们所说的完全二叉树。二叉堆他并不是二叉查找树,但是又类似于二叉查找树,他要求子节点的值得大于父节点的值,也就是说对于每一个节点都成立。而他们的兄弟节点大小顺序无关。
由此可知,但凡我们想找最小的,只用输出根节点即可!!
二叉堆算法实现
初始化,提供两个构造方法供用户使用。
public class MyBinaryHeap<AnyType extends Comparable<? super AnyType>> {
//init items
private int currentSize;
private AnyType[] myArray;
private static final int DEFAULT_SIZE=10;
//init program
public MyBinaryHeap(){this(DEFAULT_SIZE);}
public MyBinaryHeap(int defaultSize) {
currentSize=0;
//always have a node to operate
myArray= (AnyType[]) new Comparable[defaultSize+1];
}
在这里在提供一个构造方法可以让用户直接输入一个任意类型的数组
//provide a method to add array
public MyBinaryHeap(AnyType[] inArray){
currentSize=inArray.length;
myArray= (AnyType[]) new Comparable[(currentSize*2)*11/10];
int i=1;
for(AnyType item:inArray)
myArray[i++]=item;
buildHeap();
}
buildHeap是建立二叉堆,根据用户提供的初始数据,进行排列操作
//make the array become a BinaryHeap
private void buildHeap(){
for (int i = 0; i <currentSize ; i++)
initNode(i);
}
//basic methods size empty and so on
public int size(){return currentSize;}
public boolean isEmpty(){return size()==0;}
public void makeEmpty(){currentSize=0;}
插入
首先我们需要了解二叉堆的插入操作,假设当前已经有了一个二叉堆,而你需要往里面插入一个数据,为了不破坏他的结构,你会怎么做?
我们是不是会想到在最后那个位置进行插入
对,我们在最后那个位置进行插入但是如何使插入之后的又变成二叉堆呢?
上滤
这就需要我们了解一个概念叫做上滤。所谓的上滤就是让当前节点不停的根父节点进行比较,如果当前节点小于父节点,那就把父节点的值放进当前节点中,当然当前节点的值应该提前被一个变量保存。直到找到父节点小于你要插入的这个值或找到0号位置,则把他的孩子节点赋上该值或把根节点赋上该值。
如图
/*
// 从最底下建立一个新的节点,跟他的父节点进行比较,
// 若小于则把新节点的位置赋上父结点的值
// 然后hole的位置修正为父的位置继续比较直到hole为根节点的位置结束
// 这个操作被称为上滤
*/
//insert
public void insert(AnyType x){
int temp=++currentSize;
myArray[0]=x;//把最后位置的值赋上x
for(;temp>0;temp/=2 ){//上滤操作
if (x.compareTo(myArray[temp/2])<0)//与父节点进行比较 若小于父节点 若插入为第一个数据所以要保证0这个位置有值
myArray[temp]=myArray[temp/2];//把父节点的值给子节点
else
break;
}
myArray[temp]=x;
}
删除最小的
根插入一样删除最小的也用到一个叫做下滤的操作,顾名思义也就是跟上滤反着来,原理是一样的。
在我们删除最小的时候,把最后一个元素赋给根节点,然后从根节点开始下滤操作最终得到一个二叉堆。
//deleteMin
public AnyType deleteMin(){
AnyType x=findMin();
myArray[1]=myArray[currentSize--];//把最后的元素赋予第一个位置
initNode(1);
return x;
}
//find Min
public AnyType findMin(){return myArray[1];}
/*
//从根节点开始,往下找,找到比temp小的就把他往上放
//如果该节点有两个孩子节点,则比较两个孩子节点的大小,把小的拿来跟父节点比较
//如果孩子节点比temp节点大了,则把父节点设为temp 结束
//这个过程称为下滤
*/
//init node
private void initNode(int location){
int child_L;
AnyType x=myArray[location];//记录当前位置的数据
for (;location*2<currentSize;location=child_L){
child_L=2*location;
if(child_L!=currentSize&&myArray[child_L].compareTo(myArray[child_L+1])>0)
child_L++;
if(x.compareTo(myArray[child_L])>0)
myArray[location]=myArray[child_L];
else
break;
}
myArray[location]=x;
}
扩大数组
如果插入数据的数量大于二分之一数组的长度,则把数组扩大两倍。
//enlarge array
private void enLargeArray(int length){
AnyType[] old=myArray;
myArray= (AnyType[]) new Comparable[length];
for (int i = 0; i < old.length; i++)
myArray[i]=old[i];
}
}