C++ 优先级队列交换内容

Posted

技术标签:

【中文标题】C++ 优先级队列交换内容【英文标题】:C++ priority queue swapping contents 【发布时间】:2014-05-19 06:10:47 【问题描述】:

我正在编写一个具有三个优先级队列作为私有成员的类。

class Foo 
...
...

private:
  // I am fine with using pointers instead if it helps.
  std::priority_queue<int> first;   // min heap.
  std::priority_queue<int> second;  // max heap.
  std::priority_queue<int> third;  // min heap.
;

现在我要求firstthirdmin heapssecond 开头为max heap。作为课堂功能的一部分,我需要执行以下操作:

    second 移动到first。理想情况下,这是通过最少的复制量来实现的。底层向量应该被移动。此外,first 现在应该表现得像 max heap。 将third 移动到second。这意味着 second 现在应该表现得像 min heap。 由于third的内容已经移动到second,它应该是空的。我想分配一个新的底层向量或重新使用first's 底层向量(它不再需要它了。另外第三个现在应该是max heap

我需要执行这个循环(最大值 -> 最小值和最小值 -> 最大值)未知次数。

我很难用std::priority_queue 做到这一点,因为比较器是一个模板参数,这意味着我无法在运行时更改它。这使我无法将 min heap 变成 max heap

所以我的问题是:

    有没有一种方法可以让std::priority_queue 进行我的竞标而不让它变得非常丑陋? 如果不是,那么我是否可以重新组织我的班级来做同样的事情,但仍然使用std::priority_queue? 否则我是否可以重用 std 库中的大部分 heapify 逻辑来实现此目的?

【问题讨论】:

有趣且写得很好的问题,+1,我们需要更多这样的问题。 得走了,所以没有时间给出完整的答案,但是:如您所见,最大和最小 PQ 是不同的类型,因此您需要这样声明它们,不能只是重新分配。也许实际上有两个最大和两个最小堆。保留州旗以了解您在特定时间拥有的州旗。您需要为所有 PQ 提供一个通用接口,因此您需要为每个 PQ 编写一个适配器。 【参考方案1】:

有没有办法让我可以弯曲 std::priority_queue 来做我的 竞价而不让它变得非常难看?

您可以编写一个包装器来隐藏谓词并在幕后使用继承。但是,这似乎有点过头了。

如果没有,那么我是否可以重新组织我的班级来做同样的事情 东西,但仍然使用 std::priority_queue?

您可以将对队列的访问封装在函数中。然后使用 bool 或 integer 变量来检查需要访问哪个队列。

否则我可以重用 std 中的大部分 heapify 逻辑吗 库来实现这一点?

根据您的解释,这听起来是最好的选择。将每个priority_queue 存储在std::vector 中,并使用std::make_heapstd::push_heapstd::pop_heap 函数来管理堆结构。如果您将所有优先级队列保存在std::array&lt;std::vector&lt;int&gt;, 3&gt; 中,则可以使用std::rotate 来执行您描述的逻辑。此外,您还需要保留一个布尔变量来指示要用于堆操作的谓词。

【讨论】:

正是我想要的。实际上,我已经在使用 make_heap、push_heap 和 pop_heap 函数了。用于建议 std::array + std::rotate 的特殊 +1。这进一步减少了样板代码!【参考方案2】:

有状态的比较器

实际上,STL 为您的情况提供了便利:您可以pass a comparator to the constructor of the priority queue。关键思想是为比较器提供一些内部状态,该状态确定应该应用 小于 还是 大于 操作。比较器类型如下所示:

struct LessOrGreater

    explicit LessOrGreater( bool isLess ) : isLessisLess 

    bool operator()( int lhs, int rhs ) const
    
        return isLess == (lhs<rhs);
    

    bool isLess;
;

优先级队列的实际类型是

using MinOrMaxQueue =
    std::priority_queue<int,std::vector<int>,LessOrGreater>;

类实现

您的类现在可以根据这个特殊的优先级队列来实现。

class Foo 
public:
    Foo()
        : first  LessOrGreater false   // initialize as min heap
        , second LessOrGreater true    // initialize as max heap
        , third  LessOrGreater false   // initialize as min heap
    

    void op(); // The operation you explained

private:
    MinOrMaxQueue first; 
    MinOrMaxQueue second;
    MinOrMaxQueue third; 
;

现在你描述的操作可以这样实现:

void Foo::op()

    first  = std::move(second);
    second = std::move(third);
    third  = MinOrMaxQueue LessOrGreater true  ; // might throw!

使其异常安全

但是,此代码不是异常安全的。由于std::vector&lt;int&gt; 的默认构造函数可能会抛出(C++ 标准不保证这里不会失败!)op() 函数的第三行可能会抛出 Foo 对象处于无效状态。这是一个strongly exception-safe 的实现,很可能同样高效:

void Foo::op()

    MinOrMaxQueue tmp LessOrGreater true  ;
    first .swap( second );
    second.swap( third );
    third .swap( tmp );

第一行是唯一可能抛出但不会修改Foo 对象的行。所以投掷确实不可能损坏任何东西。其余三行从不抛出异常,因此该函数具有很强的异常安全性。

【讨论】:

【参考方案3】:

我认为您可以使用普通的 std::vector 作为数据存储,然后使用适配器来指定堆属性。适配器在内部保留一个 std::vector 来存储数据并存储当前比较器。让我看看这是否符合您的三个要求:

class Heap

    Heap& opertor=(Heap&& h)
    
        swap(*this, h);
        return *this;
    

    void setComparator( std::function<bool (int, int)> c)
    
        comparator = std::move(c);
    

    void insert(int x)
    
        // use std::push_heap to insert x with current comparator.
    

    void swap(Heap& h)
    
        swap(comparator, h.comparator);
        swap(data, h.data);
    
private:
    std::function<bool (int,int)> comparator;
    std::vector<int> data_;
;
    从第二个到第一个。理想情况下,这是通过最少的复制量来实现的。底层向量应该被移动。 此外,首先现在应该表现得像一个最大堆。

这可以通过调用first = std::move(second) 来完成。现在数据被移动,比较器将设置为从第二个开始,使第一个成为未来的最大堆。

    从第三个移到第二个。这意味着 second 现在应该表现得像一个最小堆。

同上。 second = std::move(thid)。比较器被重置,它的行为就像一个最小堆。

    由于第三个的内容已经移到第二个,它应该是空的。我想 > 分配一个新的底层向量或重新使用第一个底层向量(它不再需要它了。另外,第三个现在应该是一个最大堆。

移动后,第三个将处于有效但未定义的状态。您不能依赖那里的内存,因此您必须将其置于有效且已定义的状态。你可以使用third.clear(); third.resize( n )

【讨论】:

以上是关于C++ 优先级队列交换内容的主要内容,如果未能解决你的问题,请参考以下文章

华为机试真题 C++ 实现打印机队列2022.11 Q4 新题

初始化并插入优先级队列 (C++)

关于C++的权值优先队列的问题

优先级队列未正确比较 C++

c++优先队列(priority_queue)用法详解

c++优先级队列priority_queue使用lambda表达式出错问题