通过二叉堆构造优先队列比较简单,易于理解。
而带索引的优先队列是通过映射关系,将一对 { 值索引,值 } 进行映射,通过 “ 值索引 ” 构造二叉堆,堆 的排序根据是比较 “ 映射出的值 ”。
比如: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;
};