索引优先队列

来源:互联网 发布:淘宝鞋柜的鞋图片报价 编辑:程序博客网 时间:2024/04/28 03:48

1.索引优先队列的意义

索引优先队列是一个比较抽象的概念,它是一个优先队列,又带有索引,这个索引是用来干什么的呢?

在正常的队列中,我们只能访问队列头元素,整个队列中的元素我们都无法访问。那么对于这些队列中的元素,如果我们有一个映射,能够知道队列中的第m个元素到底对应我们把所有元素加入优先队列之前的哪一个,那要使用它岂不是方便许多?

我们知道,在优先队列中,一个元素加入队列之后的顺序不是固定的,有可能上浮或者下沉。那么,我们怎么知道我们加入队列的这个元素,到底在队列中的什么位置呢?

这就是索引优先队列的用途。它用一个索引数组保存了某个元素在优先队列中的位置。

例如:

prirotyQueue[i] = j; //索引优先队列的第i个元素的值是jIndex[j] = i; //那么,我们需要的这个元素j在优先队列中的位置就是i 这就是所谓的逆序数

这样,当我们需要用这个元素j的时候,我们就能直接找到j的位置是优先队列的第i个。


2.索引优先队列的实际应用

(1)从上面的内容看,好像我们知道这个索引也没什么实际用处,真的是这样吗?

考虑下面一种情况,比如李雷考了全班第一,韩梅梅考了第二。我们把全班四十个人的成绩按照高低排了优先队列。但是复核的时候,突然发现韩梅梅的成绩少算了10分,加上10分应该她是第一。那么,如果没有这个索引,我们要怎么修改已经形成的优先队列呢?

有的人可能说,很简单啊,把李雷和韩梅梅出队列,然后更改成绩,重新加入队列。

好,那么,假如韩梅梅成绩统计错了,她是全班第三十九人呢?难道要把39个人的成绩重新出队列,然后重新加入吗?这个成本代价似乎有点高。

更进一步,如果是全校四千人的队列呢?如果有一千人的成绩全算错了呢?我们要重新生成这个队列一千次?

这时候,索引优先队列就有了用武之地。如果韩梅梅的成绩错了,我们从索引里知道她是优先队列里的第二个,那么我们直接修改她的成绩,然后上浮或者下沉就可以了,要付出的代价非常小。(这里的韩梅梅就是所谓的索引,他的成绩就是key)

 注意几点:

维护的性质:
1.keys[index] = element 对于keys这个数组,一个index 对应一个元素
2.keys数组是不变的
3.qp 保存index 在 二叉堆的位置 -1代表不存在
4.pq 是一个二叉堆,可以把pq[i]看做指向keys的指针数组
5.需要exch的时候,pq的元素交换,代表二叉堆的指针交换,指向正确位置。由于keys中的位置是用index标定的,那么指向pq[i]必然会指向一个key中的一个元素pq[i]中储存的内容其实就是keys的一个index。qp数组的作用在于qp[index]要指向pq中正确的位置,那么由于pq【i】指向一个index,那么它所指向的index对应pq中的位置自然就是i了,所以才会有

qp[pq[i]] = i qp[pq[j]]= j

public class HeapTwice<Item extends Comparable<Item>>{private Item[] items;private int N;private int pq[];private int qp[];public HeapTwice(int max){items = (Item[])new Object[max+1];pq = new int[max+1];qp = new int[max+1];for(int i = 0 ; i <= max ;i++) qp[i] = -1;}public boolean isEmpty(){return N == 0;}public int size(){return N;}public boolean contains(int k){return qp[k] != -1;}public void insert(int index,Item in){if(contains(index)) throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key");N++;qp[index] = N;pq[N] = index;key[index] = in;swim(N);}//items 中元素的位置是用index标定的public boolean less(int i,int j){return items[pq[i]].compareTo(items[j]) < 0;}public void exch(int i,int j){Item swap = pq[i];pq[i] = pq[j];pq[j] = swap;//如果没有qp,这样就已经交换了二叉堆的顺序了//pq看做指针,指向items的元素位置,qp指向index对应的pq位置qp[pq[i]] = i;qp[pq[j]] = j;}//由于items 中元素的位置是用index标定的,则pq[i]的内容就是一个index,那么指向这个index的必然就是当前堆的位置i了,为了实现qp[index] 为index在pq中的位置//一直有性质qp[pq[i]]=pq[qp[i]] = ipublic void swim(int k){while(k > 1 && less(k,k/2)){exch(k,k/2);k /= 2;}}public void sink(int k){while(2*k <= N){int j = 2*k;if(j < N && less(j+1,j)) j++;if(less(k,j)) break;exch(j,k);k = j;}}    public int minIndex() {        if (N == 0) throw new NoSuchElementException("Priority queue underflow");        return pq[1];    }//最小元素的Index 根据二叉堆的性质,pq[1]就是最小元素 返回最小元素的index      public Item minItem(){   if (N == 0) throw new NoSuchElementException("Priority queue underflow");   return items[pq[1]];   }   public Item ItemOf(int index){   return items[pq[qp[index]]];//或者直接return items[i];   }   public void changeItem(int index,Item in){   if (!contains(index)) throw new NoSuchElementException("index is not in the priority queue");   items[index] = in;   swim(qp[index]);   sink(qp[index]);   }   public void increaseItem(int index,Item in){   if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");   if(items[index].compareTo(in)<=0) throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key");      items[index] = in;      sink[qp[index]];   }    public void decreaseItem(int i, Item item) {        if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");        if (Items[i].compareTo(item) <= 0)            throw new IllegalArgumentException("Calling decreaseItem() with given argument would not strictly decrease the Item");        items[i] = item;        swim(qp[i]);    }    public Item delete(int i){    if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");    int index = qp[i];//这个元素在堆中的位置    exch(index,N--);//交换堆中的位置    Item out = items[index];    swim(index);    sink(index);    qp[i] = -1;    return out;    }}


理解这种数据结构的一个较好方法是将它看成一个能够快速访问其中最小元素的数组。事实上它还要更好——它能够快速访问数组的一个特定子集中的最小元素(指所有被插入的元素)。换句话说,可以将名为pq的IndexMinPQ优先队列看做数组pq[0..N - 1]中的一部分元素的代表。将pq.insert(k, item)看做将k加入这个子集并使pq[k] = item, pq.change(k, item)则代表令pq[k] = item。这两种操作没有改变其他操作所依赖的数据结构,其中最重要的就是delMin()(删除最小元素并返回它的索引)和change()(改变数据结构中的某个元素的索引——即pq[i] = item)。这些操作在许多应用中都很重要并且依赖于对元素的引用(索引)。一般来说,当堆发生变化时,我们会用下沉(元素减小时)或上浮(元素变大时)操作来恢复堆的有序性。在这些操作中,我们可以用索引查找元素。能够定位堆中的任意元素也使我们能够在API中加入一个delete()操作。


注意:这里的swim,sink不再交换key[i],key[j]的位置,它们的位置是固定的


0 0
原创粉丝点击