数据结构(八):优先队列-索引优先

 

一、 索引优先队列

  上一章节的数据结构(八):优先队列-大小优先中,能够快速的从队列中取出最大最小值并删除,但有个缺点,就是无法通过索引快速的找到某个值,并且修改它,对于能快速根据索引查找到值的需求

  我们使用索引优先队列来实现。

二、 最小索引优先队列思路

  步骤一:实现索引优先队列,直观的想法是对数据元素添加对应的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;

              }

       }

}

posted @ 2020-11-29 16:09  纪煜楷  阅读(303)  评论(0编辑  收藏  举报