【问题标题】:Priority queue with dynamic item priorities具有动态项目优先级的优先级队列
【发布时间】:2011-01-18 07:07:17
【问题描述】:

我需要实现一个优先级队列,其中队列中项目的优先级可以更改,并且队列会自行调整,以便始终以正确的顺序删除项目。我对如何实现它有一些想法,但我确信这是一个相当常见的数据结构,所以我希望我可以使用比我更聪明的人的实现作为基础。

谁能告诉我这种优先级队列的名称,以便我知道要搜索什么,或者更好的是,为我指出一个实现?

【问题讨论】:

标签: algorithm heap priority-queue


【解决方案1】:

像这样的优先级队列通常使用其他人建议的二叉堆数据结构来实现,通常使用数组表示,但也可以使用二叉树。实际上,增加或减少堆中元素的优先级并不难。如果您知道在从队列中弹出下一个元素之前更改了许多元素的优先级,您可以暂时关闭动态重新排序,将所有元素插入堆的末尾,然后重新排序整个堆(需要付出代价O(n)) 就在元素需要被弹出之前。堆的重要之处在于,将一个数组放入堆中只需要 O(n),而排序则需要 O(n log n)。

我在一个具有动态优先级的大型项目中成功使用了这种方法。

这是我对参数化priority queue implementation in the Curl programming language 的实现。

【讨论】:

    【解决方案2】:

    一个标准的二叉堆支持 5 个操作(下面的例子假设一个最大堆):

    * find-max: return the maximum node of the heap
    * delete-max: removing the root node of the heap
    * increase-key: updating a key within the heap
    * insert: adding a new key to the heap
    * merge: joining two heaps to form a valid new heap containing all the elements of both.
    

    如您所见,在最大堆中,您可以增加任意键。在最小堆中,您可以减少任意键。不幸的是,您不能双向更改密钥,但这可以吗?如果您需要双向更改密钥,那么您可能需要考虑使用min-max-heap

    【讨论】:

    • 如果您首先需要搜索要增加的元素,我看不出二进制堆如何有效地支持增加键。由于堆中没有排序,因此查找元素需要线性时间。
    • 你必须有一个元素的引用才能使增加键和减少键有效。这是通过 Boost.Heap C++ 库 boost.org/doc/libs/1_55_0/doc/html/heap/… 中的句柄实现的
    【解决方案3】:

    我建议首先尝试使用 head-in 方法来更新优先级:

    • 从队列中删除项目
    • 以新的优先级重新插入它

    在 C++ 中,这可以使用 std::multi_map 来完成,重要的是对象必须记住它在结构中的存储位置,以便能够有效地删除自己。对于重新插入,这很困难,因为您不能假设您对优先级一无所知。

    class Item;
    
    typedef std::multi_map<int, Item*> priority_queue;
    
    class Item
    {
    public:
      void add(priority_queue& queue);
      void remove();
    
      int getPriority() const;
      void setPriority(int priority);
    
      std::string& accessData();
      const std::string& getData() const;
    
    private:
      int mPriority;
      std::string mData;
    
      priority_queue* mQueue;
      priority_queue::iterator mIterator;
    };
    
    void Item::add(priority_queue& queue)
    {
      mQueue = &queue;
      mIterator = queue.insert(std::make_pair(mPriority,this));
    }
    
    void Item::remove()
    {
      mQueue.erase(mIterator);
      mQueue = 0;
      mIterator = priority_queue::iterator();
    }
    
    void Item::setPriority(int priority)
    {
      mPriority = priority;
      if (mQueue)
      {
        priority_queue& queue = *mQueue;
        this->remove();
        this->add(queue);
      }
    }
    

    【讨论】:

    • 感谢 Matthieu,我考虑过使用这种方法,但由于更新频率的原因,它的效率不足以满足我的需求。我最终使用了一个实现,其中包含一个字典,将项目映射到队列中的索引,然后在队列上有一个方法 UpdatePosition(Item item),它查找项目索引,然后将其冒泡到新位置。然后队列有一个事件,项目注册时,它们会在优先级发生变化时通知队列。这似乎运作良好。
    【解决方案4】:

    Google has a number of answers 为您服务,包括one in Java 的实现。

    但是,这听起来像是一个家庭作业问题,所以如果是这样,我建议您先尝试自己完成这些想法,然后如果您卡在某个地方并需要一个指针,则可能会参考其他人的实现正确的方向。这样,您不太可能对其他程序员使用的精确编码方法产生“偏见”,并且更有可能理解为什么要包含每段代码以及它是如何工作的。有时,进行类似于“复制和粘贴”的释义可能有点过于诱人了。

    【讨论】:

    • 感谢 Dav,但这是标准优先级队列。如果我将一个项目添加到队列中并且它的优先级发生了变化(在队列之外),则队列的顺序可能不正确。换句话说,标准优先级队列仅在将项目添加到队列时才对它们进行排序,而不是稍后。我需要实现一个队列,该队列会随着其项目更新的优先级而更新。附言这不是作业问题,我需要将其作为一些模拟软件的一部分来实现。我对如何实现它有一些想法,但想看看是否有更好的方法。
    • 啊。好的。在这种情况下,我建议寻找 Dijkstra 算法的示例实现,该算法(如果以最有效的形式实现)需要一个可重新排序的优先级队列,因此应该有你想要的。
    【解决方案5】:

    我正在寻找完全相同的东西!

    这是我的一些想法:

    1. 由于项目的优先级不断变化, 在检索项目之前对队列进行排序是没有意义的。
    2. 所以,我们应该忘记使用优先队列。和“部分”排序 检索项目时的容器。

    并从以下 STL 排序算法中进行选择: 一种。划分 湾。稳定分区 C。第n个元素 d。部分排序 e.部分排序复制 F。种类 G。稳定排序

    partition、stable_partition 和 nth_element 是线性时间排序算法,应该是我们的第一选择。

    但是,官方Java库中似乎没有提供这些算法。因此,我会建议你使用 java.util.Collections.max/min 来做你想做的事。

    【讨论】:

      猜你喜欢
      • 2012-02-24
      • 2023-04-02
      • 1970-01-01
      • 2011-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-03
      相关资源
      最近更新 更多