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

时间:2014-05-19 06:10:48

标签: c++ c++11 priority-queue

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

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.
};

现在我需要firstthird作为min heapssecond作为max heap。作为我班级功能的一部分,我需要做以下事情:

  1. second移至first。理想情况下,这是通过最低的复制量来实现的。应该移动底层矢量。此外,first现在应该表现得像max heap
  2. third移至second。这意味着second现在应该表现得像min heap
  3. 由于third的内容已移至second,因此该内容应为空。我想分配一个新的底层向量或重用first's底层向量(它不再需要它。另外第三个现在应该是max heap
  4. 我需要执行此循环(max - &gt; min和min - &gt; max)未知次数。

    我正在努力用std::priority_queue做这个,因为Comparator是一个模板参数,这意味着我无法在运行时更改它。这使我无法将min heap变为max heap

    所以我的问题是:

    1. 我是否有办法弯曲std::priority_queue来进行竞标而不会让它变得非常难看?
    2. 如果没有,那么我是否可以重新构建我的课程来做同样的事情,但仍然使用std::priority_queue
    3. 否则,我是否可以重新使用std库中的大多数heapify逻辑来实现此目的?

3 个答案:

答案 0 :(得分:8)

  

有没有办法可以弯曲std :: priority_queue来做我的   在没有让它变得非常难看的情况下竞标?

您可以编写一个隐藏谓词并在后台使用继承的包装器。然而,这似乎有点过分了。

  

如果没有,那么我是否可以重新构建我的课程来做同样的事情   事情,但仍然使用std :: priority_queue?

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

  

否则我可以重新使用std中的大部分heapify逻辑   库来实现这个目标吗?

根据您的解释,这听起来像是最好的选择。将每个priority_queue存储在std::vector中,并使用std::make_heapstd::push_heapstd::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_;
};
  
      
  1. 第二个移到第一个。理想情况下,这是通过最低的复制量来实现的。应该移动底层矢量。   此外,首先现在应该表现得像一个最大堆。
  2.   

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

  
      
  1. 将第三名移至第二名。这意味着第二个现在应该像min heap一样。
  2.   

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

  
      
  1. 由于第三个内容已移至第二个,因此应为空。我想&gt;要么分配一个新的基础向量,要么重新使用第一个基础向量(它不再需要它。另外第三个应该是最大堆。
  2.   

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