带索引的优先队列:理解映射思维

通过二叉堆构造优先队列比较简单,易于理解。

而带索引的优先队列是通过映射关系,将一对 { 值索引,值 } 进行映射,通过 “ 值索引 ” 构造二叉堆,堆 的排序根据是比较 “ 映射出的值 ”。

比如:5 映射为 “A”,3 映射为“S”,堆中是{ 5 ,3 } 并不是根据 5 > 3 排序,而是他们的映射 “A” < “S” 排序。

如何实现映射?需要构造映射表:

1 2 3 4 5 ->下标对应值索引
? ? S ? A ->对应映射值

有了映射表,我们可以快速的根据值索引找到值,二叉堆,可以知道优先队列中的第一位是最小值(或最大值)的索引,通过映射表可以找到最小值(或最大值)。

当然为了提高效率,可以加上一个索引值表的逆表,也就是下标值映射索引值,索引值映射下标值。

听起来很绕,需要用心体会一下。

这个逆表可以让我们在知道索引值(逆表的下标)的时候,迅速找到索引值在二叉堆的位置。因为将索引值作为逆表下标,可以找到二叉堆的一个下标,此下标既是索引值在二叉堆这个优先队列表中的下标。

此时我们删除,交换,更替,判断包含,都是常数级别的时间了。

其实难就难在逆表的理解。

template <typename T>
struct IndexMinPQ
{
    //构造优先队列
    IndexMinPQ(int maxNum)
    {
        assert(maxNum >= 0);
        MaxNum = maxNum;
        Num = 0;
        KeysVector = std::vector<T>(MaxNum + 1);
        indexKey = std::vector<int>(MaxNum + 1);
        indexNum = std::vector<int>(MaxNum + 1, -1);
    }

    //插入值和值索引
    void Insert(int KeyNum, T Key)
    {
        //保证值索引介于0和最大元素数之间
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        //保证没有重复索引值
        assert(!contains(KeyNum));
        //增加元素个数值
        ++Num;
        //设置值索引的序数索引
        indexNum[KeyNum] = Num;
        //设置序数的值索引索引
        indexKey[Num] = KeyNum;
        //设置值与值索引映射
        KeysVector[KeyNum] = Key;
        //上浮序数中的值索引位置
        swim(Num);
    }

    //更换值和值索引映射
    void change(int KeyNum, T Key)
    {
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        assert(contains(KeyNum));
        //改变映射关系
        KeysVector[KeyNum] = Key;
        //由于不知道更换的值比原先大还是小,需要上浮和下沉
        swim(indexNum[KeyNum]);
        sink(indexNum[KeyNum]);
    }

    //更换值和值索引映射,值变小
    void decreaseKey(int KeyNum, T key)
    {
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        assert(contains(KeyNum));
        assert(KeysVector[KeyNum] > key);
        KeysVector[KeyNum] = key;
        swim(indexNum[KeyNum]);
    }

    //更换值和值索引映射,值变大
    void increaseKey(int KeyNum, T key)
    {
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        assert(contains(KeyNum));
        assert(KeysVector[KeyNum] < key);
        KeysVector[KeyNum] = key;
        sink(indexNum[KeyNum]);
    }

    //是否包含值索引
    bool contains(int KeyNum)
    {
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        //判断序数是否有值
        return indexNum[KeyNum] != -1;
    }

    void del(int KeyNum)
    {
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        assert(contains(KeyNum));
        //取得值索引的序数
        int indexKeyNum = indexNum[KeyNum];
        //交换序数中的值索引和最后一个值索引,并将值索引总数量减一
        exch(indexKeyNum, Num--);
        //上浮下沉交换后的值索引
        swim(indexKeyNum);
        sink(indexKeyNum);
        //被删除的值索引的序数已经超出值索引总数量,将其设为-1代表删除
        indexNum[KeyNum] = -1;
    }

    //取得最小值
    T min()
    {
        assert(Num != 0);
        //返回序数为1的值
        return KeysVector[indexKey[1]];
    }

    //取得最小值的值索引
    int minIndex()
    {
        assert(Num != 0);
        //返回序数为1的值索引
        return indexKey[1];
    }

    //删除最小值与最小值索引映射,返回最小值值索引
    int delMin()
    {
        assert(Num != 0);
        //取得序数为1的值索引
        int minKeyNum = indexKey[1];
        //交换第一个和最后一个序数中的值索引,将序数总数减一
        exch(1, Num--);
        //下沉交换后的值索引
        sink(1);
        assert(minKeyNum == indexKey[Num + 1]);
        //将最小值的值索引的序数置空
        indexNum[minKeyNum] = -1;
        //销毁最小值值索引和最小值的映射关系
        indexKey[Num + 1] = -1;
        //返回最小值的索引值
        return minKeyNum;
    }

    //返回值索引对应的值
    T keyOf(int KeyNum)
    {
        assert(KeyNum >= 0 && KeyNum < MaxNum);
        assert(contains(KeyNum));
        return KeysVector[KeyNum];
    }

    bool isEmpty() { return Num == 0; }

    int size() { return Num; }

private:
    //比较值索引序数中值索引对应值的大小
    bool greater(int indexKeyNum_i, int indexKeyNum_j)
    {
        //根据值索引序数,返回值索引,根据值索引映射值
        return KeysVector[indexKey[indexKeyNum_i]] > KeysVector[indexKey[indexKeyNum_j]];
    }

    //交换值索引序数,同时交换序数索引中的值索引
    void exch(int indexKeyNum_i, int indexKeyNum_j)
    {
        std::swap(indexKey[indexKeyNum_j], indexKey[indexKeyNum_i]);
        indexNum[indexKey[indexKeyNum_i]] = indexKeyNum_i;
        indexNum[indexKey[indexKeyNum_j]] = indexKeyNum_j;
    }

    //上浮值索引的序数位置
    void swim(int IndexKeyNum)
    {
        while (IndexKeyNum > 1 && greater(IndexKeyNum / 2, IndexKeyNum))
        {
            exch(IndexKeyNum, IndexKeyNum / 2);
            IndexKeyNum = IndexKeyNum / 2;
        }
    }

    //下沉值索引的序数位置
    void sink(int indexKeyNum)
    {
        while (2 * indexKeyNum <= Num)
        {
            int indexKeyNum_j = 2 * indexKeyNum;
            if (indexKeyNum_j < Num && greater(indexKeyNum_j, indexKeyNum_j + 1))
            {
                ++indexKeyNum_j;
            }
            if (!greater(indexKeyNum, indexKeyNum_j))
            {
                break;
            }
            exch(indexKeyNum, indexKeyNum_j);
            indexKeyNum = indexKeyNum_j;
        }
    }

    //最大可包含的值索引映射个数
    int MaxNum;
    //实际的值索引映射个数
    int Num;
    //值索引索引,通过序数找到值索引
    std::vector<int> indexKey;
    //序数索引,通过值索引的值找到值索引的序数位置
    std::vector<int> indexNum;
    //值索引和值的映射表,下标为值索引,下标中的值为值
    std::vector<T> KeysVector;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值