我们可以试着把一组数据创建为大根堆 27 15 19 18 28 34 65 49 25 37
下图是调整好的大根堆
public class TestHeap{
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem = new int[10];
}
public void createHeap(int[] arry){
//准备数据
for(int i = 0 ; i<array.length ; i++){
elem[i] = array[i];
usedSize++;
}
//写最后一棵树的根节点
for(int p = (usedSize-1-1)/2; p>=0 ; p--){
shiftDown(p,usedSize); //向下查找
}
}
//len是每颗子树调整的结束条件
//root是每颗子树调整结束的结束条件
private void shiftDown(int root,int len){
int paren = root;
int child = 2*paren+1;
while(child<len){
//如果有右孩子,找到左右孩子的最大值
if(child+1<len && elem[child]<elem[child+1]{
child++; //child下标保存的是左右孩子最大值的下标
}
//比较孩子的最大值和根节点去比较大小
if(elem[child]>elem[parent]){
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent]=tmp;
paren = child;
child = 2*parent+1;
}else{
break;
}
}
}
}
大根堆的时间复杂度O(n),建堆的时间复杂度是O(n):
则需要移动节点总的移动步数为:
T(n) = 2^0*(h-1)+2^1*(h-2)+2^2*(h-3)+2^3*(h-4)+...+2^(h-3)*2+2^(h-2)*1①
左右两边同时乘2 :
2* T(n) = 2^1*(h-1)+2^2*(h-2)+2^3*(h-3)+2^4*(h-4)+...+2^(h-2)*2+2^(h-1)*1 ②
②-①错位相减: 2-1错位相减:
T(n)=1-h+2^1 +2^2+2^3+2^4+...+2^(h-2)+2^(h-1)
T(n)=2^0+2^1+2^2+2^3+2^4+...+2^(h-2)+ 2^(h-1)-h
用等比求和公式
T(n)=2^h-1- h
最大总节点数:n=2^h-1 h= log₂(n + 1)
建大根堆的时间复杂度:T(n)=n- log2(n+ 1)≈n
堆的插入
public void push(int val){
if(isFull()){
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize]= val;
shiftUp(usedSize);
//有效数据加1;
usedSize++;
}
public boolean isFull(){
return usedSize == elem.length;
}
}
public void shiftUp(int child) {
// 找到child的双亲
int parent = (child - 1) / 2;
while (child > 0) {
// 如果双亲比孩子大,parent满足堆的性质,调整结束
if (elem[parent] > elem[child]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child-1)/2;
} else{
//如果双亲小于孩子
break;
}
}
}
测试结果:
预估结果是这样: 我们把80插入到10下标的位置,然后执行完我们的代码就变成下图.
运行结果:
出队(删除):每次删除都是优先级高的元素,出队之后依然要保持大根堆.
public boolean isFull(){
return usedSize == elem.length;
}
public void pollHeap(){
if(isEmpty()){
System.out.println("优先级队列为空!");
return; //学过抛异常也可以抛异常
}
int tmp = elem[0];
elem[0] = elem[usedSize-1];
elem[usedSize-1] = tmp;
usedSize--;
shiftDown(0,usensize);
}
public boolean isEmpty(){
return usedSize == 0;
}
以下面的第一个图为例子,我们要删除65. 最后调整完变成图二,65被删除,usedsize也变成9.
测试结果:
运行结果如下因为65是简单类型,所以不置为空;usedSize为9,所以9号元素就没有了.
获取堆顶元素
public int peekHeap(){
if(isEmpty()){
System.out.println("优先级队列为空!");
return -1;
}
return -1;
}
return elem[0];
}
}
常见题:
选A.因为堆是层序遍历的
选C,分别是 15和10比较
10和12比较
12和16比较
选C
Java中的优先级队列PriorityQueue
PriorityQueue默认是个小根堆
如果我想让PriorityQueue改成大根堆怎么办?
(这是student类,类里面本身没有comparable接口)
如果是Integer类也可以写匿名内部类来解决,重写compare的方法改成大根堆.
Integer方法自己内部有compareTo接口,compare方法.然后我再重写一遍,改变成大根堆