最大优先队列(代码实现)
定义优先队列类:
package com.ffyc.maxpriorityqueue;
//此时就是表示我们的泛型T对应的实际类型肯定是实现了Comparable接口的
public class MaxpriorityQueue <T extends Comparable<T>>{
//存储堆中元素的数组
private T[] items;
//记录堆中元素的个数
private int N;
//构造方法: 创建一个指定容量的最大优先队列
public MaxpriorityQueue(int capacity){
//这里是使用了多态的形式, 因为我们的T是存储的元素的类型, 并且T还是实现了Comparable接口的, 所以我们可以创建一个Comparable接口类型的数组, 最终再将这个数组转为T类型即可
//这里由于数组实现堆的时候牺牲了一个空间为了提升一定的效率 ---> 因为我们从索引为1的位置开始存储元素的话我们计算父节点的时候直接就是N/2,计算起来比较简单, 运算效率比较高
this.items = (T[])new Comparable[capacity + 1];
this.N = 0;
}
//获取队列中的元素的个数
public int size(){
return N;
}
//判断队列是否为空
public boolean isEmpty(){
return N == 0;
}
//判断堆中索引为i处的元素是否小于索引j处的元素
private boolean less(int i, int j){
return items[i].compareTo(items[j]) < 0;
}
//交换堆中i索引和j索引处的值
private void exch(int i, int j){
//我们交换两个位置的元素值的时候先使用一个临时变量将其中一个值保存下来, 防止失真
T temp = items[i];
items[i] = items[j];
items[j] = temp;
}
//往堆中插入一个元素
public void insert(T t){
//在数组中最后一个位置的后一个位置添加元素
items[++N] = t;
/*
但是添加了元素之后堆可能就会不满足最大优先队列的结构了, 这个时候我们就要给这个添加元素进行一个上浮算法
*/
//注意: 当我们N自加了之后, 此时N表示的是最后一个元素, 也就是新添加的元素
swim(N);
}
//删除堆中的最大元素,有返回这个最大元素
public T delMax(){
//因为要返回最大值, 所以我们要先将这个最大值保存下来
T max = items[1];
/*
我们删除堆中最大元素的时候, 为了效率, 我们要将最大值元素和最后一个元素值进行一个交换, 交换之后直接将最后一个元素删除即可,
我们删除之后最终再对堆中的第一个元素进行一个下沉算法即可
*/
exch(1,N);
//这里要注意: 我们删除堆中的元素是通过N--来完成的, 就有一些类似于指针下移的操作一样
N--;
//对第一个元素进行一个下沉,调用下沉算法即可
sink(1);
return max;
}
//使用上浮算法, 使索引k处的元素处在堆中一个正确的位置
private void swim(int k) {
//只要是遍历到的位置的结点还有根结点, 这个时候就继续遍历
while(k > 1){
//判断此结点和父节点的值的大小, 如果当前的结点值大于(或者大于等于其实都可以)父节点的值,就交换当前结点的值和当前结点的父节点的值
if(less(k/2,k)){
exch(k/2,k);
}
//我们在循环中一定要注意: 控制变量的改变
k = k/2;
}
}
//使用下沉算算法, 使索引k处的元素处在堆中一个正确的位置
private void sink(int k) {
//判断只有k位置结点有左子结点, 那么就继续判断
while(2*k <= N){
//记录左右子结点中较大值的索引的临时变量
//这里注意: 我们上浮的时候是当前结点要比父节点值中大的值大的之后就交换,我们下沉的时候是要当前结点比子节点中较大值小的时候就交换
int max;
if(2*k + 1 <= N){//如果该结点还有右子节点
if(less(2*k,2*k+1)){
//如果左子节点的值小于右子节点的值
max = 2 * k + 1;
}else{
max = 2 * k;
}
}else{
//如果此结点没有右子节点, 只有左子节点, 那么我们的max变量直接记录左子节点的值
max = 2 * k;
}
//如果k位置的值大于max索引位置处的值的时候就停止循环, 直接退出即可
if(!less(k,max)){
break;
}
exch(k,max);
//在循环中一定要注意循环变量的更替
k = max;
}
}
}
然后我们给出测试代码:
public static void main(String[] args) {
//创建优先队列
MaxpriorityQueue<String> queue = new MaxpriorityQueue<>(10);
//在队列中存储元素
queue.insert("A");
queue.insert("B");
queue.insert("C");
queue.insert("a");
queue.insert("b");
queue.insert("c");
//通过循环的方式从队列中删除最大值元素
while(!queue.isEmpty()){
String max = queue.delMax();
System.out.print(max + "");
}
}
- 这里我们的String是实现了Comparable接口的, 如果泛型类型是一个自定义类类型时, 我们就要求这个自定义类要去主动的实现Comparable接口,否则就会报错 —> 因为: public class MaxpriorityQueue <T extends Comparable< T>>