Skip to content
Related Articles

Related Articles

Improve Article

Indexed Priority Queue with Implementation

  • Difficulty Level : Expert
  • Last Updated : 04 Aug, 2021
Geek Week

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:

  1. 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) )

  2. 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))

  3. top: Return Key-Value pair to user.

    Implementation: Return Key-Value pair which is at top of the heap.

    Time Complexity: O(1)

  4. 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)

  5. 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)

  6. 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:

  1. 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.
  2. Data type of value: This is second parameter in definition.
  3. 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.
  4. 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;
  
    // 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;
}
Output:

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

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

In case you wish to attend live classes with experts, please refer DSA Live Classes for Working Professionals and Competitive Programming Live for Students.




My Personal Notes arrow_drop_up
Recommended Articles
Page :