优先队列
最大堆
最大堆是指每个根节点都大于等于左右子节点。例如下图
最小堆
最小堆是指每个根节点都小于等于左右子节点。例如下图
堆的表示
以上图最小堆为例子
它对应的数组为1,2,3,17,19,36,7,25,100
假设下标从0开始,那么1的小标是0,2的小标是1,3的小标是2.
可以发现规律若根节点下标i
那么左孩子的下标是2i+1
右孩子的下标是2i+2
入堆
只演示最小堆
假设现在堆的元素如上图所示,要把2入堆
第一步2和父亲5比较
5明显大于2,把父亲下调到2的位置,如下图所示
第二步2和3比较
3明显大于2,把3下调,如下图所示
第三步2和1比较
2>1 不需要再调整了,直接把2放在1的左边
代码如下
public boolean enque(int in) {
//不允许0是因为出堆的时候size=0的时候我想返回0,所以干脆就不允许入堆0
if(in==0)return false;
int i = size++;//当前节点的下标
if(i>=capacity)return false;
while(i>0){
int par = (i-1)/2;//父亲的下标
//flag==false 最小堆
//falg=true 最大堆
if(!flag?queue[par]<=in:queue[par]>=in)break;
queue[i] = queue[par];//把父亲的值放下来
i = par;//自己的下标提上去
}
queue[i] = in;//把入堆元素放在合适的位置
return true;
}
代码看不懂没关系,手动模拟一遍就看懂了,然后多写几遍代码就自然会写了。
出堆
只演示最小堆
如上图所示:现在要出堆
第一步暂存temp=1,用最后一个元素8代替堆顶,并重置堆尾为0,size–, 操作后如下图所示
第二步,对8进行最小堆调整,让8与左右孩子较小的交换,交换后如下图
第三步,此时8还是不满足最小堆的性质,还要对8进行调整,让8与左右孩子较小的交换,交换后如下图
出堆代码如下
public int dequeue(){
if(size==0)return 0;
int temp = queue[0];//保留 出堆的元素
--size;
queue[0] = queue[size];//最后一个元素赋给堆顶
queue[size] = 0;//最后一个置为0
//对根节点进行调整
sift(queue,0,size-1);
return temp;
}
//调整算法
//k是待调整的节点的下标
//m是最后一个元素的下标
public void sift(int[] arr,int k,int m){
int i = k;//i指向待调整的节点
int j = 2*i+1;//j先指向左孩子
while(j<=m){
if(!flag){//最小堆
if(j<m&&arr[j]>arr[j+1]){//右孩子较小,j应该指向右孩子
++j;
}
if(arr[i]<arr[j]){//已经是最小堆了,break
break;
}
}else{//最大堆
if(j<m&&arr[j]<arr[j+1]){//右孩子较大,j指向右孩子
++j;
}
if(arr[i]>arr[j]){//已经是最大堆,break
break;
}
}
swap(arr,i,j);//交换
i=j;//继续向下调整
j=2*i+1;
}
}
完整代码
public class PriorityQueue {
private int size;
private int capacity = 10;
private int[] queue;
private boolean flag;//false:默认最小堆,true代表最大堆
public PriorityQueue(boolean flag,int capacity){
this.flag = flag;
this.capacity = capacity;
queue = new int[capacity];
}
public PriorityQueue(boolean flag){
this.flag = flag;
queue = new int[capacity];
}
//默认最小堆
public PriorityQueue(int capacity){
this.capacity = capacity;
queue = new int[capacity];
}
//默认最小堆 默认capacity = 10;
public PriorityQueue(){
queue = new int[capacity];
}
public int dequeue(){
if(size==0)return 0;
int temp = queue[0];//保留 出堆的元素
--size;
queue[0] = queue[size];//最后一个元素赋给堆顶
queue[size] = 0;//最后一个置为0
//对根节点进行调整
sift(queue,0,size-1);
return temp;
}
//调整算法
//k是待调整的节点的下标
//m是最后一个元素的下标
public void sift(int[] arr,int k,int m){
int i = k;//i指向待调整的节点
int j = 2*i+1;//j先指向左孩子
while(j<=m){
if(!flag){//最小堆
if(j<m&&arr[j]>arr[j+1]){//右孩子较小,j应该指向右孩子
++j;
}
if(arr[i]<arr[j]){//已经是最小堆了,break
break;
}
}else{//最大堆
if(j<m&&arr[j]<arr[j+1]){//右孩子较大,j指向右孩子
++j;
}
if(arr[i]>arr[j]){//已经是最大堆,break
break;
}
}
swap(arr,i,j);//交换
i=j;//继续向下调整
j=2*i+1;
}
}
public static void swap(int[] arr, int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public int take() {
return size==0?0:queue[0];
}
//入堆 不允许0
public boolean enque(int in) {
if(in==0)return false;
int i = size++;//当前节点的下标
if(i>=capacity)return false;
while(i>0){
int par = (i-1)/2;//父亲的下标
//flag==false 最小堆
//falg=true 最大堆
if(!flag?queue[par]<=in:queue[par]>=in)break;
queue[i] = queue[par];//把父亲的值放下来
i = par;//自己的下标提上去
}
queue[i] = in;//把入堆元素放在合适的位置
return true;
}
public int size() {
return size;
}
public int[] getQueue(){
return queue;
}
}