堆可以用来实现优先队列,但是普通堆只支持元素的插入、删除堆顶元素,却不支持删除堆内元素、修改元素等操作。
普通的数据结构+哈希表往往能够实现一些高效的功能,比如链表+哈希表构成LRU、LFU。
堆+哈希表构成索引堆,从而可以对堆中的每个元素执行修改、删除操作。
多路归并排序需要用到堆,普通堆足以应对,因为每次删除包含最小元素的那条链,然后把这条链的指针挪动一格,重新把这条链放入堆中。整个过程只涉及:删除堆顶、添加元素两个操作。
Dijstra算法求图中某点到其它点的最短路径,也需要用到优先队列。在这个过程中,需要更新堆内元素的值,所以涉及到修改元素操作。
使用PriorityQueue+boolean数组isInQueue[]可以部分优化这个过程,它只是保证了元素不重复入堆,节省了堆所占空间。但是造成堆内元素混乱,降低了优先队列的性能。完善的解决方法就是使用索引堆。
package com.heap;
public class IndexMaxHeap {
private int[] arr;
private int[] index;
private int count;
private int capacity;
//构造方法
public IndexMaxHeap(int capacity){
this.capacity=capacity;
this.count=0;//数量初始化为0
arr=new int[capacity+1];//索引从0开始
index=new int[capacity+1];
}
//判断当前堆是否为空
public Boolean isEmpty(){
return count==0;
}
//返回该最大堆的元素个数
public int size(){
return count;
}
//插入元素到最大堆
public void insertItem(int item){
if(count+1>capacity)
System.out.println("容量已满,插入失败");
else
{
count++;
arr[count]=item;
index[count]=count;
//向上调整
shiftUp(count);
}
}
//向上调整
private void shiftUp(int k) {
//比较的是arr数组
//注意此时堆中存储的是index值,比较的是对应index值对应的arr[]数组的值
if(k>1&&arr[index[k/2]]<arr[index[k]]){
//交换的是index数组
int temp=index[k/2];
index[k/2]=index[k];
index[k]=temp;
}else
return;
k=k/2;
shiftUp(k);
}
//从堆里取出堆顶元素
public int extractMax(){
if(count<1){
System.out.println("该最大堆为空");
return -1;
}else
{
//这里取出来的是arr[]数组中的元素
//这里调整的还是index
int item=arr[index[1]];
//将末尾元素放到堆顶
index[1]=index[count];
count--;//堆的元素个数减一
//向下调整元素
shiftDown(1);
return item;
}
}
//向下调整元素
private void shiftDown(int k) {
//如果这个结点有左孩子
while(2*k<=count){
int j=2*k;
if(j+1<=count&&arr[index[j+1]]>arr[index[j]])
j+=1;
if(arr[index[j]]>arr[index[k]]){
int temp=index[j];
index[j]=index[k];
index[k]=temp;
k=j;
}else
break;
}
}
//取出最大元素的索引值
public int getMaxIndex(){
return index[1];
}
//返回给定索引在堆中所处位置对应的数据值
public int getItemByIndex(int i){
return arr[index[i]];
}
//改变给定索引对应的数据值
//别忘了改变完数据值,再去调整一下整个堆的形态
public void change(int i,int newValue){
arr[i]=newValue;//修改指定索引对应的值
//要调整改变完值的堆,必须先找到当前这个指定索引所对应的数据在堆中的位置
//我们知道在插入堆时,我们调整的是index域的位置变化,那么对应的index[j]的值就应该是i(即数组本来的索引)
//我们遍历一下index域就能找到index[j]==i;j就表示arr[i]在堆中的位置
for(int j=1;j<=count;j++){
if(index[j]==i){
//试着往上调一调,再试着往下调一调。就完成了堆的调整
shiftUp(j);
shiftDown(j);
return;//跳出多余循环
}
}
}
public static void main(String[] args) {
IndexMaxHeap heap=new IndexMaxHeap(100);
heap.insertItem(3);
heap.insertItem(15);
heap.insertItem(23);
heap.insertItem(7);
heap.insertItem(4);
heap.insertItem(8);
System.out.println("堆的大小"+heap.size());
System.out.println("堆顶元素的索引值"+heap.getMaxIndex());
System.out.println("返回索引2的值:"+heap.getItemByIndex(2));
System.out.println("按堆的顺序输出元素:");
for(int i=1;i<=heap.count;i++)
System.out.print(heap.getItemByIndex(i)+" ");
System.out.println();
heap.change(3, 66);
System.out.println("按堆的顺序输出元素:");
for(int i=1;i<=heap.count;i++)
System.out.print(heap.getItemByIndex(i)+" ");
System.out.println();
System.out.println("此时堆顶元素"+heap.extractMax());
System.out.println("此时堆顶元素"+heap.extractMax());
System.out.println("此时堆顶元素"+heap.extractMax());
System.out.println("此时堆顶元素"+heap.extractMax());
System.out.println("堆的大小"+heap.size());
}
}
参考资料
https://www.cnblogs.com/dudududu/p/8574740.html
http://shmilyaw-hotmail-com.iteye.com/blog/2097513