【问题标题】:Priority Queue with a find function - Fastest Implementation具有查找功能的优先级队列 - 最快的实现
【发布时间】:2011-04-27 19:41:37
【问题描述】:

我正在考虑实现一个带有附加要求的优先级队列,一个查找/搜索功能,它将判断一个项目是否在队列中的任何位置。所以函数将是:插入、删除和查找。

我不确定我应该使用堆还是自平衡二叉搜索树。看起来 PQ 通常是用堆实现的,但我想知道使用二叉搜索树是否有任何优势,因为我还需要那个 find 函数。

此外,平均而言,我将执行更多的插入操作而不是删除操作。我也在考虑d-ary heap。基本上,每一秒都很重要。

谢谢!

【问题讨论】:

  • “平均而言,我会进行更多的插入操作而不是删除操作”——真的是您的意思吗?如果是这样的话,你最终会耗尽内存,不是吗?
  • 优先级队列用于寻路算法。当我达到我的目标时,我可以删除优先队列的剩余部分,而无需任何重新平衡。
  • @paxdiablo - 反过来根本不可能......不是每个程序都是长期运行的
  • 您需要准确查找元素,还是只检查它是否存在于队列中?

标签: optimization types heap binary-tree priority-queue


【解决方案1】:

请检查此代码。我编写了这个程序,这是一个包含您需要的功能的优先级队列。

1. Insert
2. Find
3. Delete
4. Show

你可以试试。它运行良好。在这里,我将升序最小数字添加到最大数字。

我使用优先级队列默认功能通过开关盒来做到这一点。

queue.push()
queue.pop()
queue.top()
queue.size()

C++ 代码:

#include<bits/stdc++.h>
#include <queue>
using namespace std;
void show_queue(
    priority_queue<int, vector<int>, greater<int> > data)
{
    priority_queue<int, vector<int>,greater<int> > myq = data;
    while (!myq.empty()) {
        cout << '\t' << myq.top();
        myq.pop();
    }
    cout << '\n';
}

int main()
{
    priority_queue<int, vector<int>,greater<int> > myp_queue;
    while(1)
    {

    int choice;
    cout<<"\nwhat do you want to do?"<<endl;
    cout<<"1. Insert \n2. Find \n3. Delete \n4. Show Queue \n\nchoice your option from above: ";
    cin>>choice;

    switch(choice)
        {
            case 1:
                int n;
                cout<<"Enter the value: " ;
                cin>>n;// Option 2 => Insert
                myp_queue.push(n);
                break;
            case 2:
                if(!myp_queue.empty()){
                    cout<<"\n"<<myp_queue.top()<<" is the minimum number"<<endl; // Find the minimum number.
                }else{
                    cout<<"\nEmpty Priority Queue"<<endl;
                }
                break;
            case 3:
                if(!myp_queue.empty()){
                    myp_queue.pop(); //Delete the minimum number from the queue
                    cout<<"\nSuccessfully Deleted"<<endl;
                }else{
                    cout<<"\nThere is no element to delete"<<endl;
                }
                break;
            case 4:
                if(!myp_queue.empty()){
                    show_queue(myp_queue); // Show full queue
                }else{
                    cout<<"\nEmpty Priority Queue"<<endl;
                }
                break;
            default:
                cout<<"\nYou are terminated!!! \nYou entered wrong input.\n"<<endl;
        }

    }
    return 0;
}

【讨论】:

    【解决方案2】:

    Radix trees 具有最小堆属性将提供您需要的属性。这实际上会给您的操作带来持续的时间复杂性。例如,如果我们查看this Haskell implementation,您提到的所有三个操作都有时间复杂度O(min(n,W))。其中 n 是元素的数量,W 是 int 中的位数(32 或 64)。

    【讨论】:

      【解决方案3】:

      如果您需要多个数据结构的好处,那么您可以在组合中使用它们。 例如,如果您需要优先级队列和二叉搜索树的好处,那么对它们都执行您想要的操作。

      如果是insert,则将元素插入到它们两者中。

      如果是find,则可以使用二叉搜索树找到该元素,如果找到,则继续在优先级队列中查找。

      如果是min,则首先将其从优先级队列中删除,既然您知道它是哪个元素,那么您可以将其从二叉搜索树中删除。

      如果是del,则首先在二叉搜索树中找到它并将其删除,然后继续在优先级队列中找到它并将其也从那里删除。

      假设二叉树的节点和优先队列的节点是指向你的元素的指针。

      【讨论】:

        【解决方案4】:

        将您的数据存储在您测试过的最快的容器中,并使用布隆过滤器来测试容器中是否有东西。

        我在之前的项目中将布隆过滤器与哈希表结合使用,它在哈希表上的处理速度提高了 400 倍,平均大约 10k 项。

        布隆过滤器有一些有趣的属性:

        • 如果布隆过滤器的答案是否定的,那么它是 100% 可靠的。
        • 如果答案是肯定的,你必须检查其他数据 结构以确保项目实际存在。
        • 确保你选择了一个好的哈希函数:)

        【讨论】:

        • 你不能从布隆过滤器中删除一个元素,所以一旦你 pop(),布隆过滤器将总是在那里显示元素。最终,布隆过滤器将始终显示任何东西都在那里。
        【解决方案5】:

        如果您的查找操作相对不频繁(并且您的堆相当小),我会进行线性搜索。如果比较频繁,或者堆很大,请考虑使用单独的数据结构或对象标志来跟踪堆成员资格(以进行“查找”测试)。外部索引的乐趣在于能够将您的对象放入任意数量的容器中。

        如果您所说的“查找”真正的意思是“查找和修改”(我发现我经常需要独立于典型的插入/删除操作从优先级队列中删除内容),以下是我使用过的三种方法:

        在相当小的工作集 (500-1000) 上,考虑到插入/删除分钟的高速率(连续 100k/s)和查找删除的低速率(比如 1/s),我做了线性搜索元素,然后以标准方式将其从树中删除。

        鉴于插入/删除分钟的高比率以及相当频繁的查找删除,我只是在间接找到删除的对象后将它们标记为“无趣”。实际空闲被推迟,直到对象正常出队。

        鉴于只有几个元素和相当少的删除的小型 std::priority_queue(除了插入/删除分钟之外没有访问方法),我只是将整个队列复制到一个临时 std::vector 并复制修改/期望的部分回到队列中。然后我哭着睡着了。

        【讨论】:

          【解决方案6】:

          为什么不能只使用优先队列和集合?当您将某些东西排入队列时,您将其添加到集合中。当您将其出列时,您将其从集合中移除。这样,集合会告诉你是否有东西在队列中。

          【讨论】:

            【解决方案7】:

            在堆上的 IIRC 搜索/查找是 O(n),而在树上则是 O(log(n)),其他标准 PQ 操作是相同的。

            在经验上,堆只是通过一些常数因素更有效,所以如果它是一个大队列,那么树应该更好,如果它很小,你需要测试和分析。理论上知道什么更快,但如果这些常数因素很大,它可能与足够小的数据集完全无关。

            【讨论】:

            • 我对这个答案投了反对票,因为它是错误的。堆和搜索树支持不同的操作和不同的复杂性。 find-min 在堆中是 O(1),而在平衡搜索树中是 O(log n)。在一些堆中插入是O(1),在搜索树中插入是O(log n)。这不仅仅是理论。这些O(log n)O(1) 的复杂性可能会对性能产生巨大影响。
            猜你喜欢
            • 1970-01-01
            • 2012-04-21
            • 1970-01-01
            • 1970-01-01
            • 2023-04-02
            • 1970-01-01
            • 1970-01-01
            • 2012-11-26
            • 1970-01-01
            相关资源
            最近更新 更多