事缓则圆,人缓则安
—— 24.10.5
一、优先级队列
优先级队列 一端进,另一端出 按优先级出队
普通队列 一端进,另一端出 先进先出FIFO
二、无序数组实现
Type parameters:<E>:队列中元素的类型,必须实现Priority接口
思路
设置对象优先级Entry,每个存入数组的元素都有它的优先级,无序数组实现时,加入元素直接在数组尾部进行添加,删除元素时遍历一遍数组,寻找优先级最大的元素,将优先级最大的元素进行删除
对象优先级接口
public interface Priority {
// 返回对象的优先级,约定数字越大,优先级越高
// Returns:优先级
int priority();
}
优先级接口实现
public class Entry implements Priority{
String value;
int priority;
public Entry(String value, int priority) {
this.value = value;
this.priority = priority;
}
@Override
public int priority() {
return priority;
}
@Override
public String toString() {
return "Entry{" +
"value='" + value + '\'' +
", priority=" + priority +
'}';
}
}
队列接口
public interface Queue<E> {
/*
向队列尾部插入值
Params:value-待插入值
Returns:插入成功返回true,插入失败返回false
*/
boolean offer(E e);
/*
从队列头获取值,并移除
Returns:如果队列非空返回头部值,否则返回null
*/
E poll();
/*
从队列头获取值,不移除
Returns:如果队列非空返回队头值,否则返回null
*/
E peek();
/*
检查队列是否为空
Return:空返回true,否则返回false
*/
boolean isEmpty();
/*
检查队列是否为满
Return:满返回true,否则返回false
*/
boolean isFull();
}
无序数组实现队列接口
import Day10Queue.Queue;
import java.util.Arrays;
public class PriorityQueue1<E extends Priority> implements Queue<E> {
// 数据类型E要实现Priority接口,所以数组类型直接定义成Priority
Priority[] array;
int size;
public PriorityQueue1(int capacity) {
array = new Priority[capacity];
}
// 添加元素
@Override
public boolean offer(E e) {
if (isFull()) {
return false;
}
array[size] = e;
size++;
return true;
}
// 寻找优先级最高的索引值
private int selectMax(){
int max = 0;
for (int i = 0; i < size - 1; i++) {
// 寻找优先级最高的元素
if (array[i].priority() > array[max].priority()){
max = i;
}
}
return max;
}
// 删除元素
@Override
public E poll() {
if (isEmpty()) {
return null;
}
int max = selectMax();
E e = (E) array[max];
remove(max);
return e;
}
// 删除元素
private void remove(int index) {
if (index == size - 1) {
array[size] = null;
} else if (index < size-1) {
// 原数组 原数组起始位置 目的数组 目的数组起始位置 移动的元素个数
System.arraycopy(array,index+1,array,index,size-index-1);
}
size--;
array[size] = null;
}
// 获取元素
@Override
public E peek() {
if (isEmpty()){
return null;
}
int max = selectMax();
return (E) array[max];
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public boolean isFull() {
return size==array.length;
}
@Override
public String toString() {
return "PriorityQueue1{" +
"array=" + Arrays.toString(array) +
", size=" + size +
'}';
}
}
测试类
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.*;
public class TestPriorityQueue1 {
@Test
public void poll(){
PriorityQueue1<Entry> queue1 = new PriorityQueue1<>(5);
queue1.offer(new Entry("task1",4));
queue1.offer(new Entry("task2",3));
queue1.offer(new Entry("task3",2));
queue1.offer(new Entry("task4",5));
queue1.offer(new Entry("task5",1));
assertFalse(queue1.offer(new Entry("task6",7)));
assertEquals(5, queue1.poll().priority());
assertEquals(4, queue1.poll().priority());
assertEquals(3, queue1.poll().priority());
assertEquals(2, queue1.poll().priority());
assertEquals(1, queue1.poll().priority());
System.out.println(queue1.toString());
}
}
三、有序数组实现
Type parameters:<E>:队列中元素的类型,必须实现Priority接口
思路
将元素按照优先级进行排序存储在基于有序数组实现的队列中,删除元素时只需要在数组最尾端进行出队操作,添加元素时需要遍历一遍队列内的元素,寻找新元素优先级存储的位置,将新元素存储在其优先级按序排列的数组中的位置
对象优先级接口
public interface Priority {
// 返回对象的优先级,约定数字越大,优先级越高
// Returns:优先级
int priority();
}
优先级接口实现
public class Entry implements Priority{
String value;
int priority;
public Entry(String value, int priority) {
this.value = value;
this.priority = priority;
}
@Override
public int priority() {
return priority;
}
@Override
public String toString() {
return "Entry{" +
"value='" + value + '\'' +
", priority=" + priority +
'}';
}
}
队列接口
public interface Queue<E> {
/*
向队列尾部插入值
Params:value-待插入值
Returns:插入成功返回true,插入失败返回false
*/
boolean offer(E e);
/*
从队列头获取值,并移除
Returns:如果队列非空返回头部值,否则返回null
*/
E poll();
/*
从队列头获取值,不移除
Returns:如果队列非空返回队头值,否则返回null
*/
E peek();
/*
检查队列是否为空
Return:空返回true,否则返回false
*/
boolean isEmpty();
/*
检查队列是否为满
Return:满返回true,否则返回false
*/
boolean isFull();
}
有序数组实现队列接口
import Day10Queue.Queue;
public class PriorityQueue2<E extends Priority> implements Queue<E> {
Priority[] array;
int size;
public PriorityQueue2(int capacity) {
array = new Priority[capacity];
}
@Override
public boolean offer(E e) {
if (isFull()){
return false;
}
insert(e);
size++;
return false;
}
private void insert(E e) {
int i = size-1;
while (i >= 0 && array[i].priority() > e.priority()){
array[i+1] = array[i];
i--;
}
array[i+1] = e;
}
@Override
public E poll() {
if (isEmpty()){
return null;
}
E e = (E) array[size-1];
array[size-1] = null;
size--;
return e;
}
@Override
public E peek() {
if (isEmpty()){
return null;
}
return (E) array[array.length - 1];
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == array.length;
}
}
测试类
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class TestPriorityQueue2 {
@Test
public void poll(){
PriorityQueue2<Entry> queue1 = new PriorityQueue2<>(5);
queue1.offer(new Entry("task1",4));
queue1.offer(new Entry("task2",3));
queue1.offer(new Entry("task3",2));
queue1.offer(new Entry("task4",5));
queue1.offer(new Entry("task5",1));
assertFalse(queue1.offer(new Entry("task6",7)));
assertEquals(5, queue1.poll().priority());
assertEquals(4, queue1.poll().priority());
assertEquals(3, queue1.poll().priority());
assertEquals(2, queue1.poll().priority());
assertEquals(1, queue1.poll().priority());
System.out.println(queue1.toString());
}
}
四、堆实现
数据结构——堆
定义
计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下:
大顶堆,任意节点C与它的父节点P符合:P.value >= C.value
小顶堆,任意节点C与它的父节点P符合:P.value <= C.value
最顶层的节点(没有父亲),称之为root根节点
完全二叉树:除叶子节点那一层(最后一层)外,其余层都被填满(最后一层可填满可不填满)
特性:完全二叉树添加元素必须从左到右依次填满
特征
如果从索引0开始存储节点数据
节点 i 的父节点为 floor((i-1)/2),当i>0时
节点 i 的左子节点为 2i+ 1,右子节点为 2i + 2,当然它们需要 < size
如果从索引1开始存储节点数据
节点 i 的父节点为 floor(i/2),当i>1时
节点 i 的左子节点为 2i,右子节点为 2i + 1 ,同样需要 < size
大顶堆实现优先级队列
思路
根据大顶堆特性,root根节点就是优先级最高的元素,索引0位置就是要出队的元素,直接在队头出队进行删除
入堆新元素,加入到数组末尾(索引位置child)
不断比较新加入元素与它父亲节点(parrent)的优先级
如果父节点优先级低,则向下移动,并找到下一个parrent
直至父节点优先级更高或 child == 0 为止
交换堆顶和尾部元素,让尾部元素出队
(下潜)
从堆顶开始,将父元素与两个孩子较大者交换
直到父元素大于两个孩子,或没有孩子为止
元素优先级接口
public interface Priority {
// 返回对象的优先级,约定数字越大,优先级越高
// Returns:优先级
int priority();
}
队列接口
import Day13PriorityQueue.Priority;
public interface Queue<E> {
/*
向队列尾部插入值
Params:value-待插入值
Returns:插入成功返回true,插入失败返回false
*/
boolean offer(E e);
/*
从队列头获取值,并移除
Returns:如果队列非空返回头部值,否则返回null
*/
E poll();
/*
从队列头获取值,不移除
Returns:如果队列非空返回队头值,否则返回null
*/
E peek();
/*
检查队列是否为空
Return:空返回true,否则返回false
*/
boolean isEmpty();
/*
检查队列是否为满
Return:满返回true,否则返回false
*/
boolean isFull();
}
优先级队列的实现
import Day10Queue.Queue;
@SuppressWarnings("all")
public class PriorityQueue3<E extends Priority> implements Queue<E> {
Priority[] array;
int size;
public PriorityQueue3(int capacity) {
array = (Priority[]) new Priority[capacity];
}
@Override
public boolean offer(E e) {
/*
入堆新元素,加入到数组末尾(索引位置child)
不断比较新加入元素与它父亲节点(parrent)的优先级
如果父节点优先级低,则向下移动,并找到下一个parrent
直至父节点优先级更高或 child == 0 为止
*/
if (isFull()){
return false;
}
int child = size;
size++;
int parent = (child - 1) / 2;
while (child >0 && e.priority() > array[parent].priority()) {
array[child] = array[parent];
child = parent;
parent = (child - 1) / 2;
}
array[child] = e;
return true;
}
@Override
public E poll() {
if (isEmpty()) {
return null;
}
swap(0,size-1);
size--;
Priority e = array[size];
array[size] = null;
// 下潜
down(0);
return (E) e;
}
private void down(int parent) {
int left = 2 * parent+1;
int right = left + 1;
// 假设父元素优先级最高
int max = parent;
if (left < size && array[left].priority() > array[max].priority()) {
max = left;
}
if (right < size && array[right].priority() > array[max].priority()) {
max = right;
}
// 有孩子优先级大于父节点
if (max != parent) {
swap(max,parent);
down(max);
}
}
private void swap(int i, int j) {
Priority temp = array[i];
array[i] = array[j];
array[j] = temp;
}
@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;
}
}
测试类
import org.junit.Test;
import static org.junit.jupiter.api.Assertions.*;
public class TestPriorityQueue3 {
@Test
public void test() {
PriorityQueue3<Entry> queue = new PriorityQueue3<>(5);
queue.offer(new Entry("task1",4));
queue.offer(new Entry("task2",3));
queue.offer(new Entry("task3",2));
queue.offer(new Entry("task4",1));
queue.offer(new Entry("task5",5));
assertFalse(queue.offer(new Entry("task6",6)));
assertEquals(5,queue.poll().priority());
assertEquals(4,queue.poll().priority());
assertEquals(3,queue.poll().priority());
assertEquals(2,queue.poll().priority());
assertEquals(1,queue.poll().priority());
}
}