目录
1、介绍
2、优先级队列的接口定义
2.1、队列基础接口
3、基于无序数组实现优先级队列
4、基于有序数组实现优先级队列
5、基于堆实现优先级队列
优先级队列是指在获取队列中的数据时,按照每一个数据的优先级进行获取,不按照先进先出的规则获取了。那么每一个数据都必须有一个相应的优先级。
优先级队列添加数据和普通队列没有区别,但是在获取数据时就不是获取队列头部的数据了,而是获取优先级最高的数据。
和普通队列相比,优先级队列就是在满足普通队列的所有需求。
/**
* 队列基础接口
*
* @author zjj_admin
*/
public interface Queue {
/**
* 向队列的尾部插入数据
*
* @param value 添加数据
* @return 是否添加成功
*/
boolean offer(E value);
/**
* 从队列的头部拉取一个数据,并从队列中移除
*
* @return 返回头部数据,没有数据时返回 null
*/
E poll();
/**
* 获取队列头部数据,不移除数据
*
* @return
*/
E peek();
/**
* 判断队列是否为空
*
* @return 队列是否为空
*/
boolean isEmpty();
/**
* 队列是否已经满了
*
* @return 队列是否已满
*/
boolean isFull();
/**
* 获取队列中元素的个数
*
* @return 元素个数
*/
int size();
}
2.2、优先级接口定义
/**
* @author zjj_admin
*/
public interface Priority {
/**
* 优先级,越大说明优先级越高
*
* @return
*/
int priority();
}
使用无序数组,在数组中存储的数据是没有顺序的,所以在获取队列元素时需要手动找到优先级最大的元素。
在添加数据时比较方便。但是在获取数据时复杂一些。
/**
* 优先级队列添加数据和普通队列没有区别,但是在获取数据时就不是获取队列头部的数据了,而是获取优先级最高的数据。
*
* 基于无序数组实现优先级队列
* 使用无序数组,在添加数据时比较方便。但是在获取数据时复杂一些。
*
* @author zjj_admin
*/
public class ArrayPriorityQueue1 implements Queue {
private final Priority[] array;
private int size;
public ArrayPriorityQueue1(int capacity) {
size = 0;
array = new Priority[capacity];
}
/**
* 添加数据
*
* @param value 添加数据
* @return
*/
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
//向数组的尾部添加一个数据即可
array[size] = value;
size++;
return true;
}
/**
* 移除并返回优先级最高的一个数据
*
* @return
*/
@Override
public E poll() {
if (isEmpty()) {
return null;
}
//获取优先级最大的元素下标
int max = selectMaxPriority();
@SuppressWarnings("all")
E value = (E) array[max];
//从索引 max + 1 开始,将后面的数据整体向前移动 1 位
System.arraycopy(array, max + 1, array, max, size - max - 1);
size--;
return value;
}
/**
* 获取优先级最大的元素下标
*
* @return
*/
private int selectMaxPriority() {
int max = Integer.MIN_VALUE;
int maxIndex = 0;
for (int i = 1; i < size; i++) {
int priority = array[i].priority();
if (priority > max) {
max = priority;
maxIndex = i;
}
}
return maxIndex;
}
/**
* 返回优先级最高的一个数据
*
* @return
*/
@Override
public E peek() {
if (isEmpty()) {
return null;
}
//获取优先级最大的元素下标
int max = selectMaxPriority();
@SuppressWarnings("all")
E value = (E) array[max];
return value;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == array.length;
}
@Override
public int size() {
return size;
}
}
使用有序数组,优先级高的数据存放在数组的最后,每一次获取数据时直接那数组中最后一个数据即可。
这样在获取数据时比较简单,进而添加数据时就比较复杂了。需要将插入数据存放在合适的位置。
/**
*
* 基于有序数组实现优先级队列
* 使用有序数组,优先级高的数据存放在数组的最后,每一次获取数据时直接那数组中最后一个数据即可。
* 这样在获取数据时比较简单,进而添加数据时就比较复杂了。需要将插入数据存放在合适的位置。
*
* @author zjj_admin
*/
public class ArrayPriorityQueue2 implements Queue {
private final Priority[] array;
private int size;
public ArrayPriorityQueue2(int capacity) {
size = 0;
array = new Priority[capacity];
}
/**
* 添加数据
*
* @param value 添加数据
* @return
*/
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
// 从后向前找,获取第一个优先级小于插入数据的下标索引
int target = getTargetIndex(value.priority());
if (target == 0) {
// 当 target = 0 时,将所有的数据整体后移动一位
System.arraycopy(array, 0, array, 1, size);
array[0] = value;
} else {
//从索引 target + 1 开始,将后面的数据整体向后移动一位
// 这里也可以采用插入排序算法进行修改
System.arraycopy(array, target + 1, array, target + 2, size - target - 1);
array[target + 1] = value;
}
size++;
return true;
}
/**
* 从后向前找,获取第一个优先级小于插入数据的下标索引
*
* @return
*/
private int getTargetIndex(int priority) {
for (int i = size - 1; i >= 0; i--) {
if (array[i].priority() < priority) {
return i;
}
}
// 最后就返第一个即可
return 0;
}
/**
* 移除并返回优先级最高的一个数据
*
* @return
*/
@Override
public E poll() {
if (isEmpty()) {
return null;
}
//直接获取最后一个数据即可
@SuppressWarnings("all")
E value = (E) array[size - 1];
//删除元素
size--;
return value;
}
/**
* 返回优先级最高的一个数据
*
* @return
*/
@Override
public E peek() {
if (isEmpty()) {
return null;
}
//直接获取最后一个数据即可
@SuppressWarnings("all")
E value = (E) array[size - 1];
return value;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == array.length;
}
@Override
public int size() {
return size;
}
}
计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下
在大顶堆中,任意节点 C 与它的父节点 P 符合 P.value >= C.value
而小顶堆中,任意节点 C 与它的父节点 P 符合 P.value <= C.value
最顶层的节点(没有父亲)称之为 root 根节点
优先级队列使用大根堆更加合适,即每一个节点的值不小于孩子节点。因为优先级队列每一个获取都是获取优先级最高的数据,所以使用大根堆更加方便。
/**
* 堆一般看做一个完全二叉树
* 优先级队列使用大根堆更加合适,即每一个节点的值不小于孩子节点。
* 因为优先级队列每一个获取都是获取优先级最高的数据,所以使用大根堆更加方便。
*
* @author zjj_admin
*/
public class HeapPriorityQueue implements Queue {
private final E[] array;
private int size;
@SuppressWarnings("all")
public HeapPriorityQueue(int capacity) {
size = 0;
array = (E[]) new Priority[capacity];
}
/**
* 添加数据
* 步骤:
* 1、将新添加的元素存在完全二叉树的最后一个节点
* 2、调整堆的结构让其满足大根堆的特点
*
* @param value 添加数据
* @return
*/
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
if (size == 0) {
array[size] = value;
size++;
return true;
}
//获取添加节点的索引
int addIndex = size;
size++;
//获取父节点的索引
int parentIndex = (addIndex - 1) / 2;
//对堆的结构进行调整,让其满足大根堆的特性。
while (addIndex > 0 && array[parentIndex].priority() < value.priority()) {
array[addIndex] = array[parentIndex];
addIndex = parentIndex;
parentIndex = (addIndex - 1) / 2;
}
array[addIndex] = value;
return true;
}
/**
* 移除并返回优先级最高的一个数据
* 步骤:
* 1、获取第一个数据,第一个数据及时需要移除的数据。
* 2、将第一个数据和最后一个数据进行交换,让需要移除的数据放在数组的最后面。
* 3、将堆的最后一个数据移除
* 4、将根节点按照大根堆的特点调整位置,让其满足大根堆。
*
* @return
*/
@SuppressWarnings("all")
@Override
public E poll() {
if (isEmpty()) {
return null;
}
//获取需要移除的位置
E removed = array[0];
//将数组的长度减少一个单位
size--;
//将最后一个放在第一个位置
array[0] = array[size];
//将最后一个数据设置为 null ,方便垃圾回收
array[size] = null;
//调整,是指满足大根堆的条件,做下潜操作
down(0);
return removed;
}
/**
* 下潜操作,知道满足大根堆的条件
*
* @param index
*/
private void down(int index) {
if (2 * index + 1 < size && array[index].priority() < array[2 * index + 1].priority()) {
// 交换位置
exchange(index, 2 * index + 1);
// 做下潜操作
down(2 * index + 1);
} else if (2 * index + 2 < size && array[index].priority() < array[2 * index + 2].priority()) {
// 交换位置
exchange(index, 2 * index + 2);
// 做下潜操作
down(2 * index + 2);
}
}
/**
* 交换索引 i 和索引 j
*
* @param i
* @param j
*/
private void exchange(int i, int j) {
E temp = array[i];
array[i] = array[j];
array[j] = temp;
}
/**
* 返回优先级最高的一个数据
*
* @return
*/
@SuppressWarnings("all")
@Override
public E peek() {
if (isEmpty()) {
return null;
}
return (E) array[0];
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == array.length;
}
@Override
public int size() {
return size;
}
}