索引优先队列

C++构造索引优先队列
博客介绍了索引优先队列,指出普通优先队列在修改元素时操作复杂,而索引优先队列通过给元素加索引可随机访问。还展示了用C++构造索引优先队列,包括类构造,插入值时构造堆比较元素值大小,索引标记元素值,方便通过索引访问元素,最后给出测试代码。

索引优先队列

上篇讲述了优先队列,一种能够快速访问最大元素或最小元素的数据结构,但是考虑这样一种情况,公司按姓名录入员工信息,并按照薪水加入了优先队列,某员工薪水需要更改,假设薪水排名为n,那么我们需要怎样操作呢?首先将前n名员工出列,然后改正第n名薪水,最后将这n名员工入列,操作太复杂,不符合程序员追求性能(懒)的特性,于是想到将优先队列的元素加一个索引,可以随机访问.这种加入索引的优先队列就叫做索引优先队列.

用c++构造索引优先队列如下:

类构造

class IndexPriorQueue{
private:
    int index;//the num of items
    int size;//capacity
    int* pq;//index binaryheap
    int* qp;//qp[pq[i]]=pq[qp[i]]=i
    T* item;//item array;
public:
    IndexPriorQueue(int qsize){//constructor function
        size=qsize;
        index=0;
        pq=new int[size+1];
        qp=new int[size+1];
        item=new T[size+1];
        for(int i=0;i<size+1;i++)
            qp[i]=-1;

    }
    void insert(int k,T v){
        if(contain(k)){
            cout<<"index is already in queue"<<endl;
            return;
        }
        //cout<<"insert"<<endl;
        item[k]=v;
        pq[++index]=k;
        qp[k]=index;
        swim(index);
    }
    bool contain(int k){
        return qp[k]!=-1?1:0;
    }
    void deleteat(int k){
        int j=qp[k];
        pq[j]=pq[index];
        index--;
        swim(j);
        sink(j);
        qp[k]=-1;
    }
    T delmax(){
        int k=pq[1];
        T max=item[pq[1]];
        pq[1]=pq[index--];
        sink(1);
        qp[k]=-1;
        return max;
    }
    void swim(int j){
        while(j>1){
            if(less(j/2,j)){
                exch(j/2,j);
                j=j/2;
            }else{
                break;
            }
        }
    }
    void sink(int j){

        while(2*j<=index){
            int k=2*j;
            if(k<index&&less(k,k+1))
                k++;
            if(less(j,k))
                exch(j,k);
            else
                break;
            j=k;
        }
    }
    bool less(int i,int j)
    {
        return item[pq[i]]<item[pq[j]]?1:0;
    }
    void exch(int m,int n){
        int temp=pq[m];
        pq[m]=pq[n];
        pq[n]=temp;
        qp[pq[m]]=m;
        qp[pq[n]]=n;
    }
    void display(){
        cout<<"item:";
        for(int i=1;i<size+1;i++){
            cout<<item[i]<<"  ";
        }
        cout<<endl;
        cout<<"pq:";
        for(int i=1;i<size+1;i++){
            cout<<pq[i]<<"  ";
        }
        cout<<endl;
        cout<<"qp:";
        for(int i=1;i<size+1;i++){
            cout<<qp[i]<<"  ";
        }
        cout<<endl;

    }
};

其中,每次插入值时,构造堆比较的是元素值的大小,然后索引会标记元素值,这样可以通过索引非常方便的访问元素.

测试代码

int main(){
    cout<<"before insert:"<<endl;
    IndexPriorQueue<char> ipq(10);
    ipq.display();
    ipq.insert(1,'a');
    ipq.insert(2,'d');
    ipq.insert(3,'c');
    ipq.insert(5,'z');
    ipq.insert(6,'x');
    cout<<"after insert:"<<endl;
    ipq.display();
    cout<<"delmax:";
    cout<<ipq.delmax()<<endl;;
    ipq.display();
    ipq.insert(5,'y');
    ipq.display();
    ipq.deleteat(6);
    cout<<"deleteat";
    ipq.display();
    return 0;
}
### 3.1 索引优先队列的实现思路 索引优先队列是一种特殊的优先队列,它允许通过索引来访问和操作队列中的元素。与传统的优先队列不同,索引优先队列不仅支持插入和删除操作,还支持更新操作,这使得它在某些应用场景中非常有用,例如图算法中的最短路径问题。 #### 数据结构设计 索引优先队列的设计需要维护三个主要数组: - `pq[]`:存储堆中的元素索引。 - `qp[]`:反向映射,存储每个索引在堆中的位置。 - `keys[]`:存储每个索引对应的优先级值。 为了实现索引优先队列,首先需要定义这些数组并初始化它们。堆的根节点从索引 `1` 开始,这样可以方便地进行堆操作。 ```java public class IndexMinPQ<Key extends Comparable<Key>> { private int maxN; // 最大容量 private int n; // 当前元素数量 private int[] pq; // 堆数组 private int[] qp; // 反向映射数组 private Key[] keys; // 优先级值数组 public IndexMinPQ(int maxN) { this.maxN = maxN; n = 0; pq = new int[maxN + 1]; qp = new int[maxN]; keys = (Key[]) new Comparable[maxN]; for (int i = 0; i < maxN; i++) { qp[i] = -1; // 初始化为-1,表示该索引不在队列中 } } } ``` #### 插入元素 插入元素时,需要将元素的索引和优先级值分别存储到 `pq` 和 `keys` 数组中,并更新反向映射数组 `qp`。插入操作后,需要通过上浮操作来维护堆的性质。 ```java public void insert(int i, Key key) { if (contains(i)) { throw new IllegalArgumentException("索引 " + i + " 已经存在于队列中"); } n++; qp[i] = n; pq[n] = i; keys[i] = key; swim(n); } ``` #### 删除最小元素 删除最小元素的操作实际上是删除堆顶元素。这个过程包括将堆顶元素与最后一个元素交换,然后调整堆的结构以维持堆的性质。 ```java public int delMin() { if (n == 0) { throw new NoSuchElementException("队列为空"); } int minIndex = pq[1]; exch(1, n); n--; sink(1); qp[minIndex] = -1; // 标记为不在队列中 return minIndex; } ``` #### 更新元素 更新元素的优先级时,需要找到该元素在堆中的位置,并更新其优先级值,然后通过上浮或下沉操作来恢复堆的性质。 ```java public void decreaseKey(int i, Key key) { if (!contains(i)) { throw new NoSuchElementException("索引 " + i + " 不在队列中"); } if (keys[i].compareTo(key) <= 0) { throw new IllegalArgumentException("新键不小于当前键"); } keys[i] = key; swim(qp[i]); } ``` #### 辅助方法 - **上浮操作**:将指定位置的元素向上移动,直到满足堆的性质。 - **下沉操作**:将指定位置的元素向下移动,直到满足堆的性质。 - **交换操作**:交换堆中两个位置的元素。 - **检查索引是否存在**:检查指定的索引是否存在于队列中。 ```java private void swim(int k) { while (k > 1 && greater(k / 2, k)) { exch(k, k / 2); k = k / 2; } } private void sink(int k) { while (2 * k <= n) { int j = 2 * k; if (j < n && greater(j, j + 1)) { j++; } if (!greater(k, j)) { break; } exch(k, j); k = j; } } private boolean greater(int i, int j) { return keys[pq[i]].compareTo(keys[pq[j]]) > 0; } private void exch(int i, int j) { int swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; qp[pq[i]] = i; qp[pq[j]] = j; } public boolean contains(int i) { return qp[i] != -1; } ``` 通过这些方法,可以实现一个完整的索引优先队列。这种方法不仅提高了操作的效率,还使得程序更加简洁和易于理解。索引优先队列在处理需要频繁更新优先级的应用场景中非常有用,例如任务调度和图算法中的最短路径问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值