带实现的索引优先级队列
优先级队列是一种数据结构,其中数据根据其优先级进行存储。在索引优先级队列中,数据的存储方式与标准优先级队列一样,并且可以使用其键更新数据的值。之所以称为“索引”,是因为可以使用哈希映射将索引存储在容器中,使用键值对输入的键作为哈希映射的键。这在使用最小堆实现 Dijkstra 算法时很方便。它也可以用于任何其他需要将键值对放入优先级队列的程序中,并且您需要使用 push 或 pop函数更新键的值。
索引优先级队列中的操作:可以在索引优先级队列中执行的操作有:
- push :根据优先级在索引优先级队列中添加键值对。
实现:为此,在容器中添加键值对,然后根据键值对中的值进行堆化。
时间复杂度: O( log(n) )
- pop:移除最高优先级的键值对。
实现:移除堆顶,然后堆化容器的其余部分。
时间复杂度: O(log(n))
- top :将键值对返回给用户。
实现:返回堆顶的键值对。
时间复杂度: O(1)
- size :返回索引优先级队列中键值对的数量。
实现:跟踪类中队列中的元素数量,并在调用 size()函数时返回变量。
时间复杂度: O(1)
- empty :当索引优先级队列为空时返回 true。
实现:当元素的数量变量等于零时返回真。
时间复杂度: O(1)
- changeAtKey :此函数将索引优先级队列与标准优先级队列区分开来。它从用户那里获取两个参数,第一个是键,第二个是新值,它将与键关联的旧值更新为提供的新值,并根据新值的优先级更新其位置。
实现:保留一个哈希映射,其键是键值对中的键,它指向容器中键的索引。当调用该函数时,根据其优先级更新所需索引处的值并定位当前元素,最后更改哈希图中的索引值。
时间复杂度: O( log(n) )
索引优先队列的实现:
索引优先队列是使用二叉堆实现的,但也可以使用斐波那契堆或 K-ary 堆来实现。
在定义索引优先级队列的实例时需要传递四个参数(两个强制和两个可选),它们是:
- 键的数据类型:这是定义中的第一个参数,它应该是可以在散列映射中散列的数据类型,或者用户有自己的散列函数作为第四个参数。要了解有关哈希映射中哈希函数的更多信息,请参阅本文。
- 值的数据类型:这是定义中的第二个参数。
- 比较器:这是第三个可选参数。默认情况下,索引优先队列将使用最大堆来实现,以更改用户必须通过不同的比较器及其参数(即比较器的参数)作为值的数据类型。
- 散列函数:这是第四个参数,仅当用户为键传递自定义数据类型(如类)时才需要,然后用户必须传递自己的散列函数。
下面是索引优先队列的实现:
C++
// C++ program for the above approach
#include
using namespace std;
template ,
class Hash = hash >
class indexed_priority_queue {
// Storing indices of values using key
unordered_map m;
// Container
vector > 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 >& 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 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 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 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 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 IPQ)
{
indexed_priority_queue temp = IPQ;
while (!IPQ.empty()) {
pair 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 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;
}
Checking if initially the IPQ is empty
IPQ is empty
Inserting pairs (2, 1), (3, 7), (1, 0) and (4, 5)
IPQ: ( 3, 7 ) ( 4, 5 ) ( 2, 1 ) ( 1, 0 )
Size: 4
Top: 3, 7
Changing value associated with key 3 to 2 and 1 to 9
Size: 4
Top: 1, 9
Poping an element from IPQ:
Poping an element from IPQ:
IPQ: ( 3, 2 ) ( 2, 1 )
Size: 2
Top: 3, 2