优先队列
普通队列:先进先出,后进后出 优先队列:与入队和出队顺序无关,只与规定的优先级有关。 优先队列的队首元素是优先级最高的元素 优先队列可以用不同的底层数据结构来实现,只是各实现方式的时间复杂度不同而已,通常用堆实现
底层数据结构 | 出队 | 入队 |
---|
链式结构 | O(1) | O(n) | 顺序结构 | O(n) | O(1) | 堆 | O(logn) | O(logn) |
下面优先队列的实现均使用堆的数据结构
优先队列按照其作用的不同分为下面两种 最大优先队列:可以获取并删除队列中最大的值 最小优先队列,可以获取并删除队列中最小的值
相关知识 数据结构与算法(java):线性表-堆 数据结构与算法(java):线性表-队列
最大优先队列
特点: 最大的元素放在数组的索引1处,每个结点的数据总是大于等于它的两个子结点的数据。(因为用到的底层数据结构是堆,所以处理起来很像在处理堆)
代码如下
public class MaxPriorityQueue <T extends Comparable<T>>{
private T[] items;
private int Num;
public MaxPriorityQueue(int capacity) {
this.items = (T []) new Comparable[capacity+1];
this.Num = 0;
}
public int size(){
return Num;
}
public boolean isEmpty(){
return Num == 0;
}
private boolean less(int i, int j){
return items[i].compareTo(items[j])<0;
}
private void exchange(int i, int j){
T temp = items[i];
items[i] = items[j];
items[j] = temp;
}
public void insert(T t){
items[++Num] = t;
swim(Num);
}
public T delMax(){
T max = items[1];
exchange(1,Num);
Num--;
sink(1);
return max;
}
private void swim(int k){
while(k>1){
if(less(k/2,k)){
exchange(k/2,k);
}
k = k/2;
}
}
private void sink(int k){
while(2*k<=Num){
int max;
if(2*k+1<=Num){
if(less(2*k,2*k+1)){
max = 2*k+1;
}else{
max = 2*k;
}
}else{
max = 2*k;
}
if(!less(k,max)){
break;
}
exchange(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("D");
queue.insert("E");
queue.insert("F");
queue.insert("G");
while(!queue.isEmpty()){
String max = queue.delMax();
System.out.println(max + " ");
}
}
结果:
G F E D C B A
最小优先队列
与最大优先队列相反,它将最小的元素放在数组的索引1处,每个结点的数据总是小于等于它的两个子结点的数据。实现过程差不多。。。 代码如下
public class MinPriorityQueue<T extends Comparable<T>> {
private T[] items;
private int Num;
public MinPriorityQueue(int capacity){
this.items = (T[]) new Comparable[capacity + 1];
this.Num = 0;
}
public int size(){
return -1;
}
public boolean isEmpty(){
return Num == 0;
}
private boolean less(int i, int j){
return items[i].compareTo(items[j])<0;
}
private void exchange(int i, int j){
T temp = items[i];
items[i] = items[j];
items[j] = temp;
}
public void insert(T t){
items[++Num] = t;
swim(Num);
}
public T delMin(){
T min = items[1];
exchange(1,Num);
Num--;
sink(1);
return min;
}
private void swim(int k){
while(k > 1){
if(less(k,k/2)){
exchange(k,k/2);
}
k = k/2;
}
}
private void sink(int k){
while(2*k<=Num){
int min;
if(2*k+1<=Num){
if(less(2*k,2*k+1)){
min = 2*k;
}else{
min = 2*k+1;
}
}else{
min = 2*k;
}
if(less(k,min)){
break;
}
exchange(k,min);
k = min;
}
}
}
测试类
public static void main(String[] args) {
MinPriorityQueue<String> queue = new MinPriorityQueue(10);
queue.insert("E");
queue.insert("F");
queue.insert("D");
queue.insert("G");
queue.insert("A");
queue.insert("B");
queue.insert("C");
while(!queue.isEmpty()){
String min = queue.delMin();
System.out.print(min + " ");
}
}
结果
A B C D E F G
索引优先队列
仔细的了解了上述的最大优先和最小优先队列的时候可以发现,这两个队列都可以快速的访问到队列中的最大元素和最小元素,但是他们都有一个缺点,那就是无法通过索引访问已经存在于优先队列中的队列,并且更新他们,为了实现这个目的,再优先队列的基础上实现了索引优先队列。。。
下面以索引优先队列来举例。。。
原理和思路
1、要想通过某个数来获取指定的数据元素,那么就必须把这两绑定在一起,让他们相互关联,我们可以通过数组来实现,通过数组的索引来获取对应的值。因此,设置一个数组T[] items来存储数据元素,在往队列中完成插入时,绑定好数据和其索引。
2、完成上一步后,虽然给每个元素关联了一个整数,并且可以使用这个整数快速获取到该元素,但是items数组中元素都是随机的,并没有按照从小到大或者从大到小的顺序排列好,为了排好序,增加一个数组int[] pq来保存每个元素在items数组中的索引,通过让pq数组变有序,使得在拿到items数组中的元素时也是有序的,举例来说就是pq[1]对应的数据元素items[pq[1]]要小于等于pq[2]和pq[3]对应的数据元素items[pq[2]]和items[pq[3]]。
pq数组中存放的数据是items数组排序后的对应数据的索引。如果堆要调整,那么就可直接调整pq数组,通过pq数组可以找到item数组中的数据。但此时还有个弊端,当items数组中某个值修改了之后,需要找到pq数组中对应items数组中修改值的索引并进行修改,当数据较少的时候可以选择通过遍历来找到pq中存放items的索引的那个值,但当数据很大的时候,遍历的效率就会很低。
3、为了较快速的找到pq中存放的items数组的索引,有增加了一个数组qp,这个数组用来存放pq的逆序,就是说, pq[6] = 1, qp[1] = 6; 当有了pq数组后,如果要修改items[0] = “H”,那么就可以先通过索引0,在qp数组中找到qp的索引:qp[0] = 9;之后可以直接调整pq[9]即可。
具体代码实现如下
public class indexMinPriorityQueue<T extends Comparable<T>> {
private T[] items;
private int[] pq;
private int[] qp;
private int N;
public indexMinPriorityQueue(int capacity) {
this.items = (T[]) new Comparable[capacity + 1];
this.pq = new int[capacity + 1];
this.qp = new int[capacity + 1];
this.N = 0;
for (int i = 0; i < qp.length; i++) {
qp[i] = -1;
}
}
public int size(){
return N;
}
public boolean isEmpty(){
return N == 0;
}
private boolean less(int i, int j){
return items[pq[i]].compareTo(items[pq[j]])<0;
}
private void exchange(int i, int j){
int temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
qp[pq[i]] = i;
qp[pq[j]] = j;
}
public boolean contains(int k){
return qp[k] != -1;
}
public int minIndex(){
return pq[1];
}
public void insert(int i, T t){
if(contains(i)){
return ;
}
N++;
items[i] = t;
pq[N] = i;
qp[i] = N;
swim(N);
}
public int delMin(){
int minIndex = pq[1];
exchange(1,N);
qp[pq[N]] = -1;
pq[N] = -1;
items[minIndex] = null;
N--;
sink(1);
return N;
}
public void delete(int i){
int k = qp[i];
exchange(k,N);
qp[pq[N]] = -1;
pq[N] = -1;
items[k] = null;
N--;
sink(k);
swim(k);
}
public void revise(int i, T t){
items[i] = t;
int k = qp[i];
sink(k);
swim(k);
}
private void swim(int k){
while(k>1){
if(less(k,k/2)){
exchange(k,k/2);
}
k = k/2;
}
}
private void sink(int k){
while(2*k<=N){
int min;
if(2*k+1<=N){
if(less(2*k, 2*k+1)){
min = 2*k;
}else{
min = 2*k+1;
}
}else{
min = 2*k;
}
if(less(k,min)){
break;
}
exchange(k,min);
k = min;
}
}
}
测试代码
public static void main(String[] args) {
indexMinPriorityQueue<String> queue = new indexMinPriorityQueue<>(10);
queue.insert(0,"A");
queue.insert(1,"C");
queue.insert(2,"F");
queue.revise(2,"8");
while(!queue.isEmpty()){
int index = queue.delMin();
System.out.println(index + "");
}
}
结果:
2 1 0
|