个人主页:Lei宝啊
愿所有美好如期而遇
Container就是适配器,也就是说这个优先级队列的底层就使用vector,当然,我们也可以使用deque来适配,但是对于优先级队列来说,效率是低于vector的,因为优先级队列本质就是堆,我们需要用到下标来进行向上或者向下调整,而deque虽然可以使用下标索引,同时也具备链式结构,但是单独拉出来一个功能效率是不如vector和queue的,所以使用deque适配的效率不如vector,同时这里是不能用queue来适配的。
上面我们也可以看见Compare默认是less,也就是说优先级队列默认是建大堆的(尽管使用less建大堆,greater建小堆看着很难受,但库就是这么实现的)。
push方法,就是向优先级队列里插入元素,同时调整成堆,pop方法,就是删除堆顶元素,同时向下调整维持堆的结构。
其他方法我们不多赘述,接下来开始模拟实现优先级队列。
首先将Compare仿函数实现出来,这里就要介绍什么是仿函数:
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
我们平时调用函数时是这样:比如有一个int add(int a,int b)函数,我们调用时是这样add(1,2),那么仿函数就是:类里重载了()运算符的operator(),使得这个类使用起来像一个函数。
我们使用时先创建这个类的对象,比如Less compare; 然后比较大小就是这样:compare(1,2),是不是很类似于函数?
接着就是优先级队列的内部构造:
其中的核心也就是堆向上调整,堆向下调整,数据的插入和删除,也就这么四个核心点,type就是我们要适配的容器。
//默认建大堆
template<class T, class type = vector<int>, class compare = Less<T>>
class priority_queue
{
public:
priority_queue() {};
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
q.push_back(*first);
judge_up(size() - 1);
first++;
}
}
bool empty() const
{
return q.empty();
}
int size() const
{
return q.size();
}
T& top()
{
return q.front();
}
const T& top() const
{
return q.front();
}
void judge_up(int child)
{
int parent = (child - 1) / 2;
while (child)
{
if (com(q[child], q[parent]))
{
swap(q[child], q[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& x)
{
q.push_back(x);
judge_up(size() - 1);
}
void judge_down(int parent)
{
int child = parent * 2 + 1;
while (child < size())
{
if (child + 1 < size() && !com(q[child], q[child + 1]))
{
child++;
}
if (com(q[child], q[parent]))
{
swap(q[child], q[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void pop()
{
swap(q[0], q[size() - 1]);
q.erase(q.end() - 1);
judge_down(0);
}
private:
type q;
compare com;
};
}