数据结构(八):优先队列-索引优先
一、 索引优先队列
上一章节的数据结构(八):优先队列-大小优先中,能够快速的从队列中取出最大最小值并删除,但有个缺点,就是无法通过索引快速的找到某个值,并且修改它,对于能快速根据索引查找到值的需求
我们使用索引优先队列来实现。
二、 最小索引优先队列思路
步骤一:实现索引优先队列,直观的想法是对数据元素添加对应的key,方便通过key找到value,由于优先队列我们使用数组实现,因此可以使用索引-值的方式来表示,如下图:
items[]:{ "S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E" }
这样我们便能通过items[i]获取到值
步骤二:
步骤一将元素存储至堆数组后,发现不符合堆有序,此时若对items数组进行堆有序调整,则会破坏元素在items的下标,再通过items[i]获取时会有问题,此时我们引入一个辅助数组pq[]
pq的存放元素在 items中的索引,对堆进行有序处理时,只需处理pq[i]即可:
有序后的堆:
此时的pq数组即是一个有序堆
步骤三:
通过步骤二的调整后,我们对堆有序的调整,只需通过pq进行调整,items元素保持不变,当我们做出items[0]=J的操作后,只需通过遍历pq数组,对pq[9]做上浮和下沉调整即可,保证pq堆有序
但在数据比较大的场景下,上述的遍历找到pq[i]的成本就变得很高,此时我们引入另一个辅助数组qp[],qp的存在是为了更快找到pq中值对应的items的索引,此时我们可以对pq做一个倒转
及qp的索引是pq的值,qp的值是pq的索引,见下图:
有了qp数组后,当我们做出items[0]=J的操作后,只需通过qp[0]获取到9,再去对pq[9]做上浮和下沉处理,及可在修改队列元素的同时,又保证堆有序。
三、 代码实现
如下代码基于如上三个步骤实现了索引最小优先队列:
/** * 索引最小优先队列 * * @author jiyukai * @param <T> */ public class IndexMinPriorityQueue<T extends Comparable<T>> {
// 存储具体元素的堆 private T[] items;
// 保存每个元素在items中的索引,pq需要堆有序 private int[] pq;
// 保存pq数组索引和值的逆序 private int[] qp;
// 堆中元素的个数 private int N;
/** * 初始化索引优先队列的大小 * * @param size */ public IndexMinPriorityQueue(int size) { items = (T[]) new Comparable[size + 1]; N = 0; pq = new int[size + 1]; qp = new int[size + 1];
for (int i = 0; i < qp.length; i++) { qp[i] = -1; } }
/** * 优先队列大小 * * @return */ public int size() { return N; }
/** * 是否为空 * * @return */ public boolean isEmpty() { return N == 0; }
/** * 元素大小比较 * * @param i * @param j * @return */ public boolean bigger(int i, int j) { return items[i].compareTo(items[j]) > 0; }
/** * 位置交换 * * @param i * @param j */ private void switchPos(int i, int j) { int temp = pq[i]; pq[i] = pq[j]; pq[j] = temp;
// 修改qp qp[pq[i]] = i; qp[pq[j]] = j; }
/** * 判断k位置对应的元素是否存在 * * @param k * @return */ public boolean contains(int k) { return qp[k] != -1; }
/** * 最小元素的索引 * * @return */ public int minIndex() { return pq[1]; }
/** * i位置处插入元素t * * @param i * @param t * @throws Exception */ public void insert(int i, T t) throws Exception { // 判断位置i处是否存在元素t if (contains(i)) { throw new Exception(i + "位置处已有元素" + t); } // item的i处插入t items[i] = t; // 元素个数+1 N++; // 插入到最后一个元素,因此pq的N位置处为item的i pq[N] = i; // 修改qp的i位置处为N qp[i] = N; // 上浮处理N,让堆有序 swim(N); }
/** * 删除最小的元素并返回这个元素关联的索引 * * @return */ public int delMin() { // 获取items中最小的元素的索引 int min = pq[1]; // 交换最小元素和N位置处的元素 switchPos(1, N); // 删除qp qp[pq[N]] = -1; // 删除pq pq[N] = -1; // N位置处的元素置空,即删除掉最小元素 items[min] = null; // 元素个数减1 N--; // 对1位置处的元素进行下沉处理 sink(1);
return min; }
/** * 删除索引i关联的元素 * * @param i */ public void delete(int i) { // 获取items i位置的元素 int index = pq[i]; // 交换最小元素和N位置处的元素 switchPos(index, N); // 删除qp qp[pq[N]] = -1; // 删除pq pq[N] = -1; // N位置处的元素置空,即删除掉最小元素 items[i] = null; // 元素个数减1 N--; // 对i位置处的元素进行下沉处理 sink(index);
// 对i位置处的元素进行上浮处理 swim(index); }
/** * 把索引i处的元素修改为t * * @param i * @param t */ public void changeItem(int i, T t) { //修改items中i位置处的元素为t items[i] = t; //获取i位置处元素在pq中的索引 int k = pq[i]; //上浮 swim(k); //下沉 sink(k); }
/** * 上浮k位置处的元素 * * @param k */ private void swim(int k) { while(k>1) { if(bigger(k, k/2)) { switchPos(k, k/2); }
k = k/2; } }
/** * 下沉k位置处的元素 * * @param k */ private void sink(int k) { while(2 * k <= N) { int min = 2 * k; if(bigger(2*k, 2*k+1)) { min = 2*k+1; }
if(bigger(min,k)) { break; }
switchPos(min, k);
k = min; } } } |