📜  带实现的索引优先级队列

📅  最后修改于: 2022-05-13 01:55:52.453000             🧑  作者: Mango

带实现的索引优先级队列

优先级队列是一种数据结构,其中数据根据其优先级进行存储。在索引优先级队列中,数据的存储方式与标准优先级队列一样,并且可以使用其键更新数据的值。之所以称为“索引”,是因为可以使用哈希映射将索引存储在容器中,使用键值对输入的键作为哈希映射的键。这在使用最小堆实现 Dijkstra 算法时很方便。它也可以用于任何其他需要将键值对放入优先级队列的程序中,并且您需要使用 push 或 pop函数更新键的值。

索引优先级队列中的操作:可以在索引优先级队列中执行的操作有:

  1. push根据优先级在索引优先级队列中添加键值对。

    实现:为此,在容器中添加键值对,然后根据键值对中的值进行堆化。

    时间复杂度: O( log(n) )

  2. pop:移除最高优先级的键值对。

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

    时间复杂度: O(log(n))

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

    实现:返回堆顶的键值对。

    时间复杂度: O(1)

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

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

    时间复杂度: O(1)

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

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

    时间复杂度: O(1)

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

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

    时间复杂度: O( log(n) )

索引优先队列的实现:

索引优先队列是使用二叉堆实现的,但也可以使用斐波那契堆或 K-ary 堆来实现。

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

  1. 键的数据类型:这是定义中的第一个参数,它应该是可以在散列映射中散列的数据类型,或者用户有自己的散列函数作为第四个参数。要了解有关哈希映射中哈希函数的更多信息,请参阅本文。
  2. 值的数据类型:这是定义中的第二个参数。
  3. 比较器:这是第三个可选参数。默认情况下,索引优先队列将使用最大堆来实现,以更改用户必须通过不同的比较器及其参数(即比较器的参数)作为值的数据类型。
  4. 散列函数:这是第四个参数,仅当用户为键传递自定义数据类型(如类)时才需要,然后用户必须传递自己的散列函数。

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

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;
}


输出: