C++ 在C++;

C++ 在C++;,c++,algorithm,data-structures,C++,Algorithm,Data Structures,有时在编程竞赛等过程中,我们需要一个简单的带有减少键的最小优先级队列的工作实现来实现Dijkstra算法等。。我经常使用set和一个数组(映射ID-->key_值)来实现这一点 向集合中添加元素需要O(log(N))时间。为了从N个元素中构建优先级队列,我们只需将它们逐个添加到集合中。这总共需要O(N log(N))个时间 具有min key_值的元素只是集合的第一个元素。探测最小的元素需要O(1)个时间。删除它需要O(log(N))时间 为了测试某个ID=k是否在集合中,我们首先在数组中查找

有时在编程竞赛等过程中,我们需要一个简单的带有减少键的最小优先级队列的工作实现来实现Dijkstra算法等。。我经常使用set和一个数组(映射ID-->key_值)来实现这一点

  • 向集合中添加元素需要O(log(N))时间。为了从N个元素中构建优先级队列,我们只需将它们逐个添加到集合中。这总共需要O(N log(N))个时间

  • 具有min key_值的元素只是集合的第一个元素。探测最小的元素需要O(1)个时间。删除它需要O(log(N))时间

  • 为了测试某个ID=k是否在集合中,我们首先在数组中查找它的key_value=v_k,然后搜索集合中的元素(v_k,k)。这需要O(log(N))时间

  • 要将某个ID=k的key_值从v_k更改为v_k',我们首先在数组中查找其key_值=v_k,然后在集合中搜索元素(v_k,k)。接下来,我们从集合中删除该元素,然后将该元素(v_k',k)插入集合中。然后我们也会更新数组。这需要O(log(N))时间

尽管上述方法可行,但大多数教科书通常建议使用二进制堆来实现优先级队列,因为构建二进制堆的时间仅为O(N)。我听说C++中有一个使用二进制堆的STL内建优先级队列数据结构。但是,我不确定如何更新该数据结构的key_值

在C++中使用密钥更新的最小优先级队列最简单最有效的方法是什么?

< P>我不认为类允许高效实现<代码>减少键< /代码>样式操作。 我使用了自己的基于二进制堆的数据结构来支持这一点,基本上与您描述的基于
std::set
的优先级队列非常相似:

  • 维护一个二进制堆,按
    排序,存储
    对的元素
    和一个映射
    ID->heap\u索引
    的数组。在堆例程中,有必要确保映射数组与元素的当前堆位置保持同步。这增加了一些额外的
    O(1)
    开销
  • 根据所描述的标准算法,可以在
    O(N)
    中完成数组到堆的转换
  • 窥视根元素是
    O(1)
  • 检查
    ID
    当前是否在堆中只需要在映射数组中进行
    O(1)
    查找。这还允许
    O(1)
    查看与任何
    ID
    对应的元素
  • reduce key
    需要在映射数组中查找
    O(1)
    然后查找
    O(log(N))
    通过
    heapify\u up、heapify\u down更新堆
  • 将新项推送到堆上是
    O(log(N))
    就像从堆中弹出一个现有项一样
因此,与基于
std::set
的数据结构相比,对于一些操作,运行时会逐渐得到改进。另一个重要的改进是二进制堆可以在数组上实现,而二进制树是基于节点的容器。二进制堆的额外数据局部性通常转化为改进的运行时

此实施中的几个问题是:

  • 它只允许整数项
    ID
  • 它假设item
    ID
    的分布紧密,从零开始(否则映射数组的空间复杂度就会增加!)
如果您维护一个映射哈希表而不是一个映射数组,那么您可能会克服这些问题,但运行时开销会稍微大一些。对于我来说,整数
ID
就足够了


希望这能有所帮助。

正如达伦所说,没有降低元素优先级的方法,也没有删除当前最小值以外的元素的方法。但是默认的
std::priority\u队列
不过是一个简单的容器适配器,它围绕着使用
中的std堆函数的
std::vector
,和)。所以对于Dijkstra(需要优先级更新的地方),我通常自己做,并使用一个简单的
std::vector

推送就是O(logn)操作

vec.push_back(item);
std::push_heap(vec.begin(), vec.end());
当然,为了从N个元素重新构造一个队列,我们不会使用这个O(logn)操作来推送它们(使整个事情成为O(nlogn)),而是将它们全部放在向量中,后跟一个简单的O(N)

min元素是一个简单的O(1)

pop是简单的O(logn)序列

到目前为止,这正是
std::priority_queue
通常在引擎盖下做的事情。现在要更改项的优先级,我们只需要更改它(但是它可能会合并到项的类型中),并使序列再次成为有效堆

std::make_heap(vec.begin(), vec.end());
我知道这是一个O(N)操作,但另一方面,这消除了使用额外的数据结构或(更糟糕的是)项目类型的扩展来跟踪项目在堆中的位置的任何需要。考虑到
std::vector
(这也会影响运行时)的易用性、紧凑性和线性内存使用,以及我经常处理边数较少(顶点计数为线性)的图,对数优先级更新的性能损失实际上并没有那么大

这可能不是所有情况下最快的方法,但肯定是最简单的方法


编辑:哦,由于标准库使用最大堆,您需要使用与
等效的方法来比较优先级(无论您从项目中获得优先级),而不是默认的
,尽管我的回答不会回答原始问题,我认为这可能对那些在tr时遇到这个问题的人有用
vec.front();
std::pop_heap(vec.begin(), vec.end());
vec.pop_back();
std::make_heap(vec.begin(), vec.end());
#include <bits/stdc++.h>

using namespace std;

vector<int> queue_idx;

struct Elem {
    int label;
    int dist;
    bool operator<(const Elem& other) const { return dist > other.dist; }
    Elem& operator=(const Elem& other);
};

vector<Elem> q;

Elem& Elem::operator=(const Elem& other)
{
    label = other.label;
    dist = other.dist;
    queue_idx[label] = this - q.data();
    return *this;
}

void AddElem(int label, int dist)
{
    queue_idx[label] = label;
    q.push_back(Elem{label, dist});
}

void RemoveMin()
{
    pop_heap(q.begin(), q.end());
    Elem res = q.back();
    q.pop_back();
    cout << "dist to " << res.label << " is " << res.dist << endl;
}

void Relax(int label, int dist)
{
    int idx = queue_idx[label];
    Elem& elem = q[idx];
    if (elem.dist > dist)
    {
        elem.dist = dist;
        push_heap(q.begin(), q.begin() + idx + 1);
    }
}

int main()
{
    int n = 5;
    queue_idx.resize(n);
    AddElem(0, 0);
    for (int i = 1; i < n; ++i)
        AddElem(i, INT_MAX);
    make_heap(q.begin(), q.end());
    RemoveMin();
    Relax(1, 50);
    Relax(2, 40);
    Relax(4, 10);
    RemoveMin();
    Relax(3, 20);
    RemoveMin();
    Relax(1, 30);
    RemoveMin();
    Relax(2, 80);
    RemoveMin();
    return 0;
}
void build_min_PQ(long int *arr, long int n)
{
   for(long int i = n/2; i>0; i--)
   {
       adjust_PQ(arr, n, i);
   }    
}
void delete_element_pq(long int* arr, long int n, long int index)
{
    long int left_i = 2*index, right_i = 2*index+1;
    cout<<"index = "<<index<<" n = "<<n;nl
    arr[1] = arr[n];
    n--;
    adjust_PQ(arr, n, 1);
}
void adjust_parent(long int* arr, long int n, long int index)
{
    long int parent;
    parent = index/2;
    if(parent > 0)
    {
        if(arr[index] < arr[parent])
        {
            long int temp;
            temp = arr[parent];
            arr[parent] = arr[index];
            arr[index] = temp;
            adjust_parent(arr, n, parent);
        }
    }
}
void decrease_key(long int* arr, long int n, long int new_key, long int index)        
 //  initially index = 1    //assuming array with sufficient size
{
    arr[index] = new_key;
    adjust_parent(arr, n, index);
}
void insert_pq(long int* arr, long int n, long int key)
{
    arr[n+1] = INT_MAX;
    decrease_key(arr, n+1, key, n+1);
}
long int ext_min(long int* arr, long int n)
{
    if(n>0)
    {
        long int temp = arr[1];
        delete_element_pq(arr, n, 1);
        return temp;
    }
    return -1;            // if PQ is empty;
}