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.
;
现在我要求first
和third
以min heaps
和second
开头为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_heap
、std::push_heap
和std::pop_heap
函数来管理堆结构。如果您将所有优先级队列保存在std::array<std::vector<int>, 3>
中,则可以使用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<int>
的默认构造函数可能会抛出(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++ 优先级队列交换内容的主要内容,如果未能解决你的问题,请参考以下文章