【译】带有索引的优先队列的实施

338 阅读6分钟

索引优先队列与实施

  • 最后更新: 2021年8月4日

优先级队列是一种数据结构,其中的数据是根据其优先级存储的。在索引优先级队列中,数据的存储方式与标准优先级队列相同,同时,数据的值可以使用其键值进行更新。它被称为 "索引",因为哈希图可以用来存储容器中的索引,使用键值对输入的键作为哈希图的键。这在使用min-heap实现Dijkstra算法时很方便。它也可以用在任何其他需要将键值对放入优先队列的程序中,同时你需要用push或pop函数来更新键值。

**索引优先队列中的操作。**在索引优先级队列中可以进行的操作有。

  1. 推动 。 在索引的优先级队列中根据其优先级添加键值对。

    _实现。要_做到这一点,在容器中添加键值对,然后根据键值对的值进行堆积。

    _时间复杂度。_O( log(n) )

  2. 流行。 移除最高优先级的键值对。

    _实现方法。_移除堆的顶部,然后堆化容器的其余部分。

    _时间复杂度。_O( log(n))

  3. 顶部:将键值对返回给用户。

    _实现。返_回位于堆顶的键值对。

    _时间复杂度。_O(1)

  4. 大小:返回索引优先级队列中的键值对的数量。

    _实现:跟踪队列中的元素数量,并在调用size()函数时返回该变量。_保持跟踪类中队列中的元素数量,并在调用size()函数时返回该变量。

    _时间复杂度。_O(1)

  5. :: 当索引优先级队列为空时,返回true。

    _实现:当元素数变量等于零时,返回真。_当元素数变量等于零时返回真。

    _时间复杂度。_O(1)

  6. changeAtKey:这个函数将索引优先级队列与标准优先级队列区分开来。它从用户那里得到两个参数,第一个是键,第二个是新值,它将与键相关的旧值更新为提供的新值,并根据新值的优先级更新其位置。

    _实现。保_持一个哈希映射,其键是键值对中的键,它指向容器中键的索引。当函数被调用时,更新所需索引的值,并根据其优先级定位当前元素,最后改变哈希图中的索引值。

    _时间复杂度。_O( log(n) )

索引式优先级队列的实现。

索引式优先级队列是用二进制堆实现的,但是它也可以用斐波那契堆K-ary堆实现

在定义索引优先级队列的实例时,有四个参数需要传递(两个必须的,两个可选的),它们是:。

  1. 键的数据类型。 这是定义中的第一个参数,它应该是可以在哈希图中被哈希的数据类型,或者用户可以把自己的哈希函数作为第四个参数。要了解更多关于哈希图中的哈希函数,请看这篇文章。
  2. 值的数据类型。这是定义中的第二个参数。
  3. 比较器。这是第三个参数,也是可选参数。默认情况下,索引优先级队列将使用最大堆来实现,用户必须将不同的比较器与它的参数(即比较器的参数)作为值的数据类型。
  4. 哈希函数。这是第四个参数,只有当用户为键传递自定义数据类型(如类)时才需要,然后用户必须传递自己的哈希函数。

下面是索引优先队列的实现。

C++

// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
template <class T1,class T2,
class Comparator = less<T2>,
class Hash = hash<T1> >
class indexed_priority_queue {
// Storing indices of values using key
unordered_map<T1,long long int, Hash> m;
// Container
vector<pair<T1, T2> > v;
// Size
long long numberOfElement;
// Creating a instance of Comparator class
Comparator comp;
// Max Capacity
long long capacity = LLONG_MAX;
// Obtaing the index value from hash map
long long int getValueIndex(T1 key)
{
if (m[key] == 0) {
cout <<"No Such Key Exist";
return -1;
}
return v[m[key] - 1];
}
// heapify the container
void heapify(vector<pair<T1, T2> >& v,
long long int heap_size,
long long index)
{
long long leftChild = 2 * index + 1,
rightChild = 2 * index + 2,
suitableNode = index;
if (leftChild < heap_size
&& comp(v[suitableNode].second,
v[leftChild].second)) {
suitableNode = leftChild;
}
if (rightChild < heap_size
&& comp(v[suitableNode].second,
v[rightChild].second)) {
suitableNode = rightChild;
}
if (suitableNode != index) {
// swap the value
pair<T1, T2> temp = v[index];
v[index] = v[suitableNode];
v[suitableNode] = temp;
// updating the map
m[v[index].first] = index + 1;
m[v[suitableNode].first]
= suitableNode + 1;
// heapify other affected nodes
heapify(v, numberOfElement,
suitableNode);
}
}
public:
indexed_priority_queue()
{
numberOfElement = 0;
m.clear();
v.clear();
}
void push(T1 key, T2 value)
{
if (numberOfElement == capacity) {
cout <<"Overflow";
return;
}
if (m[key] != 0) {
cout <<"Element Already Exists";
return;
}
// Adding element
v.push_back(make_pair(key, value));
numberOfElement++;
m[key] = numberOfElement;
long long index = numberOfElement - 1;
// Comparing to parent node
while (index != 0
&& comp(v[(index - 1) / 2].second,
v[index].second)) {
// swap the value
pair<T1, T2> temp = v[index];
v[index] = v[(index - 1) / 2];
v[(index - 1) / 2] = temp;
// updating the map
m[v[index].first] = index + 1;
m[v[(index - 1) / 2].first]
= (index - 1) / 2 + 1;
// updating index in map
index = (index - 1) / 2;
}
}
void pop()
{
if (numberOfElement == 0) {
cout <<"UnderFlow";
return;
}
// Removing element
v.erase(v.begin());
numberOfElement--;
heapify(v, numberOfElement, 0);
}
pair<T1, T2> top() {return v[0]; }
long long int size() {return numberOfElement; }
bool empty() {return numberOfElement == 0; }
void changeAtKey(T1 key, T2 value)
{
if (m[key] == 0) {
cout <<"No Such Key Exist";
return;
}
long long index = m[key] - 1;
v[index].second = value;
// Comparing to child nodes
heapify(v, numberOfElement, index);
// Comparing to Parent Node
while (index != 0
&& comp(v[(index - 1) / 2].second,
v[index].second)) {
// swap the value
pair<T1, T2> temp = v[index];
v[index] = v[(index - 1) / 2];
v[(index - 1) / 2] = temp;
// updating the map
m[v[index].first] = index + 1;
m[v[(index - 1) / 2].first]
= (index - 1) / 2 + 1;
// updating index in map
index = (index - 1) / 2;
}
}
};
void display(indexed_priority_queue<int,int> IPQ)
{
indexed_priority_queue<int,int> temp = IPQ;
while (!IPQ.empty()) {
pair<int,int> tmp;
tmp = IPQ.top();
IPQ.pop();
cout <<"( " << tmp.first <<", "
<< tmp.second <<" ) ";
}
cout <<'\n';
}
// Driver Code
int main()
{
// First parameter is key datatype
// and it should be hashable
// Second parameter is value datatype comparator
// function (by default it implements maxheap)
indexed_priority_queue<int,int> IPQ;
// Check if empty
cout <<"Checking if initially the IPQ is empty\n";
if (IPQ.empty())
cout <<"IPQ is empty\n";
else
cout <<"IPQ is not empty\n";
// Insertion
cout <<"Inserting pairs (2, 1), (3, 7), "
<<" (1, 0) and (4, 5)\n";
IPQ.push(2, 1);
IPQ.push(3, 7);
IPQ.push(1, 0);
IPQ.push(4, 5);
// Printing the contents of IPQ
cout <<"IPQ: ";
display(IPQ);
cout <<'\n';
// Checking size and top after pushing
cout <<"Size: " << IPQ.size() << endl;
cout <<"Top: " << IPQ.top().first
<<", " << IPQ.top().second
<<"\n\n";
// Replace operation
cout <<"Changing value associated with"
<<" key 3 to 2 and 1 to 9\n";
IPQ.changeAtKey(3, 2);
IPQ.changeAtKey(1, 9);
// Checking size and top after replacement
cout <<"Size: " << IPQ.size() << endl;
cout <<"Top: " << IPQ.top().first
<<", " << IPQ.top().second
<<"\n\n";
// Deleting 2 elements from IPQ
cout <<"Poping an element from IPQ: ";
IPQ.pop();
cout <<"\nPoping an element from IPQ: ";
IPQ.pop();
cout <<'\n\n';
// Printing the contents of IPQ after deletion
cout <<"IPQ: ";
display(IPQ);
cout <<'\n';
// Checking size and top after pushing
cout <<"Size: " << IPQ.size() << endl;
cout <<"Top: " << IPQ.top().first
<<", " << IPQ.top().second
<<"\n\n";
return 0;
}

输出。

检查最初IPQ是否为空
IPQ为空

插入对(2,1),(3,7),(1,0)和(4,5)
IPQ: ( 3,7 ) ( 4,5 ) ( 2,1 ) ( 1,0 )

大小:4
顶部:3, 7

将与键3相关的值改为2,将1改为9
大小:4
顶部:1,9

从IPQ弹出一个元素:
从IPQ弹出一个元素。

IPQ: ( 3, 2 ) ( 2, 1 )

大小: 2
顶部: 3, 2

读者请注意!现在不要停止学习。掌握所有重要的DSA概念。 DSA自学课程以适合学生的价格获得所有重要的DSA概念,并成为行业的准备者。 要完成从学习语言到DS Algo以及更多的准备工作,请参考 完整的面试准备课程.

如果你想参加专家的现场课程 ,请参考 面向在职人士的DSA现场课程面向学生的竞争性编程直播.

我的个人笔记 arrow_drop_up

保存