我正在编写一个有三个优先级队列作为私有成员的类。
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
。我需要执行此循环(max - &gt; min和min - &gt; max)未知次数。
我正在努力用std::priority_queue
做这个,因为Comparator是一个模板参数,这意味着我无法在运行时更改它。这使我无法将min heap
变为max heap
。
所以我的问题是:
std::priority_queue
来进行竞标而不会让它变得非常难看?std::priority_queue
?heapify
逻辑来实现此目的?答案 0 :(得分:8)
有没有办法可以弯曲std :: priority_queue来做我的 在没有让它变得非常难看的情况下竞标?
您可以编写一个隐藏谓词并在后台使用继承的包装器。然而,这似乎有点过分了。
如果没有,那么我是否可以重新构建我的课程来做同样的事情 事情,但仍然使用std :: priority_queue?
您可以在函数中包装对队列的访问。然后使用bool或整数变量来检查需要访问的队列。
否则我可以重新使用std中的大部分heapify逻辑 库来实现这个目标吗?
根据您的解释,这听起来像是最好的选择。将每个priority_queue
存储在std::vector
中,并使用std::make_heap
,std::push_heap
和std::pop_heap
函数来管理堆结构。如果将所有优先级队列保留在std::array<std::vector<int>, 3>
中,则可以使用std::rotate
来执行所描述的逻辑。此外,您需要保留一个布尔变量,指示哪个谓词用于堆操作。
答案 1 :(得分:3)
实际上,STL为您的情况提供了设施:您可以pass a comparator to the constructor of the priority queue。关键思想是给比较器一些内部状态,确定是否应该应用小于或大于的操作。比较器类型如下所示:
struct LessOrGreater
{
explicit LessOrGreater( bool isLess ) : isLess{isLess} {}
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
对象。因此投掷不会损害任何东西。其余三行从不抛出,因此该函数非常安全。
答案 2 :(得分:1)
我认为您可以使用普通的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)
来完成。现在数据被移动,比较器将被设置为第二个,从而在未来首先成为最大堆。
- 将第三名移至第二名。这意味着第二个现在应该像min heap一样。
醇>
与上述相同。 second = std::move(thid)
。比较器被重置,它的行为就像一个最小堆。
- 由于第三个内容已移至第二个,因此应为空。我想&gt;要么分配一个新的基础向量,要么重新使用第一个基础向量(它不再需要它。另外第三个应该是最大堆。
醇>
移动后,第三个将处于有效但未定义的状态。您不能依赖于那里的内存,因此您必须将其置于有效且已定义的状态。您可以使用third.clear(); third.resize( n )
。