Indexed Priority Queue with Implementation

12 Jul, 2022
Priority queue is a data structure in which data is stored on basis of its priority. In an Indexed Priority Queue, data is stored just like standard priority queue and along with this, the value of a data can be updated using its key. It is called “indexed” because  a hash map can be used to store the index in container using the key of key-value pair input  as the key of hash map. This comes handy while implementing  Dijkstra’s Algorithm using min-heap. It can also be used in any other program where it is required to put key-value pairs in priority queue and along with it you need to update value of keys with push or pop function .
Operations in an Indexed Priority Queue: The operations which can be performed in Indexed Priority Queue are:
push:  Adding key-value pair in Indexed Priority Queue according to its priority. Implementation: To do this, add the key value pair in container and then heapify on basis of value in key-value pair. Time Complexity: O( log(n) )pop:  Removing the highest priority key-value pair. Implementation: Remove top of heap and then heapify rest of the container. Time Complexity: O( log(n))top: Return Key-Value pair to user. Implementation: Return Key-Value pair which is at top of the heap. Time Complexity: O(1)size: Return the number of key-value pairs in the Indexed Priority Queue. Implementation: Keep a track if number of elements in queue in the class and return the variable when size() function is called. Time Complexity: O(1)empty: Return true when Indexed Priority Queue is empty. Implementation: Return true when number of elements variable is equal to zero. Time Complexity: O(1)changeAtKey: This function differentiate Indexed Priority Queue from standard priority queue. It takes two arguments from user, first is key and second is new value, and it update old value associated with the key to new value provided and update its position according to priority of new value. Implementation: Keep a hash-map whose key is key in key-value pair and it points to index of the key in container. When the function is called update the value at required index and position current element according to its priority and finally change index value in hash map. Time Complexity: O( log(n) )
push:  Adding key-value pair in Indexed Priority Queue according to its priority. Implementation: To do this, add the key value pair in container and then heapify on basis of value in key-value pair. Time Complexity: O( log(n) )
pop:  Removing the highest priority key-value pair. Implementation: Remove top of heap and then heapify rest of the container. Time Complexity: O( log(n))
top: Return Key-Value pair to user. Implementation: Return Key-Value pair which is at top of the heap. Time Complexity: O(1)
size: Return the number of key-value pairs in the Indexed Priority Queue. Implementation: Keep a track if number of elements in queue in the class and return the variable when size() function is called. Time Complexity: O(1)
empty: Return true when Indexed Priority Queue is empty. Implementation: Return true when number of elements variable is equal to zero. Time Complexity: O(1)
changeAtKey: This function differentiate Indexed Priority Queue from standard priority queue. It takes two arguments from user, first is key and second is new value, and it update old value associated with the key to new value provided and update its position according to priority of new value. Implementation: Keep a hash-map whose key is key in key-value pair and it points to index of the key in container. When the function is called update the value at required index and position current element according to its priority and finally change index value in hash map. Time Complexity: O( log(n) )
Implementation of Indexed Priority Queue:
Indexed Priority Queue is implemented using binary heap, however it can also be implemented using Fibonacci heap or K-ary heap. 
There are four parameters to be passed while defining an instance of Indexed Priority Queue(two mandatories and two optional) which are:
Data type of key:  This is the first parameter in the definition and it should be data type that can be hashed in hash map or user have pass his own hash function as fourth parameter. To learn more about hash function in hash map see this article.Data type of value: This is second parameter in definition.Comparator: This is third and optional parameter. By default Indexed Priority Queue will be implemented using max heap to change that user have to pass different comparator with its parameter (i.e. parameter of comparator) as the data type of value.Hash function: This is fourth parameter and required only when user pass custom data type (like a class) for key, then user have to pass his own hash function.
Data type of key:  This is the first parameter in the definition and it should be data type that can be hashed in hash map or user have pass his own hash function as fourth parameter. To learn more about hash function in hash map see this article.
Data type of value: This is second parameter in definition.
Comparator: This is third and optional parameter. By default Indexed Priority Queue will be implemented using max heap to change that user have to pass different comparator with its parameter (i.e. parameter of comparator) as the data type of value.
Hash function: This is fourth parameter and required only when user pass custom data type (like a class) for key, then user have to pass his own hash function.
Below is the implementation of Indexed Priority Queue:
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;     // Obtaining 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 Codeint 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;}
priority-queue
Algorithms
Data Structures
Data Structures
priority-queue
Algorithms
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.