目录
priority_queue 的本质就是堆,以下来介绍优先级队列的模拟实现
一、优先级队列的模板参数列表
定义优先级队列类,模板参数分别为存入元素的类型T,
存放元素的底层结构默认使用std命名空间中的vector<T>、
以及比较方式less<T>,也给出了less类、以及Greater类
template<class T>
class less
{
public:
bool operator()(const T& left, const T& right)const
{
return left < right;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& left, const T& right)const
{
return left > right;
}
};
template <class T, class Contioner = std::vector<T>, class Compare = less<T>>
class priority_queue
{
};
二、优先级队列的构造函数(建堆 时间复杂度为nlogn)
这一步最重要的就是建堆,具体思路就是从优先级队列(堆)的第一个非叶子结点开始来进行向下调整,直到将顶点元素也完成向下调整。
首先利用类中对数组(默认Container使用vector<T>)_con中的vector的区间构造函数来对_con进行初始化。
然后对从第一个非叶子结点的位置进行向下调整直到将堆顶元素也进行向下调整为止。
class priority_queue
{
public:
priority_queue()
{}
template<class Iterator>
priority_queue(Iterator first, Iterator last)
: _con(first, last) // 1、使用vector的区间构造函数来初始化_con
{
// 2、建堆:从完全二叉树的最后一个非叶子结点来进行向下调整
for (int i = (size() - 2) / 2; i >= 0; i--)
{
AdjustDown(i);
}
}
private:
Container _con;
};
AdjustDown() 向下调整:
在parent的右孩子存在的条件下,选择俩个孩子中较大的那个来进行与parent的比较,如果parent位置元素小于child位置元素,就进行交换,parent和child都不断向下更新重复判断,直到parent大于child或者child超出访问范围。
建堆的时间复杂度:
为向下调整的时间复杂度logn * 遍历元素的n 即为 nlogn
private:
void AdjustDown(int parent)
{
size_t sz = size();
int child = 2 * parent - 1;
Compare com;
while (child < sz)
{
// 如果左孩子存在 且 左孩子小于右孩子 就将child进行更新
if (child + 1 < sz && com(_con[child], _con[child + 1]))
child += 1;
if (com(_con[parent], _con[child]))
{
std::swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 - 1;
}
else
return;
}
}
三、pop()接口 (堆顶元素的删除: logn)
即为固定的3步走:看下方代码即可。
void pop()
{
// 1、将堆顶元素与最后一个元素进行位置交换
std::swap(_con[0], _con[size() - 1]);
// 2、将换到最后一个位置的堆顶元素进行尾删
_con.pop_back();
// 3、将新的堆顶元素进行向下调整
AdjustDown(0);
}
四、push()接口 (logn)
将待插入元素尾插进_con,然后对该元素进行向上调整即可
void push(T val)
{
// 1、将代插入的元素进行尾部插入
_con.push_back(val);
// 2、将该元素进行向上调整
AdjustUp();
}
AdjustUp() 向下调整:
void AdjustDown(size_t parent)
{
size_t sz = size();
size_t child = 2 * parent + 1;
Compare com;
while (child < sz)
{
// 如果左孩子存在 且 左孩子小于右孩子 就将child进行更新
if (child + 1 < sz && com(_con[child], _con[child + 1]))
child += 1;
if (com(_con[parent], _con[child]))
{
std::swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
五、其他函数
size_t size()
{
return _con.size();
}
const T& top()const
{
return _con[0];
}
bool empty()const
{
return _con.empty();
}
六、源码及测试:
#include<iostream>
#include<vector>
using namespace std;
namespace wei
{
template<class T>
class less
{
public:
bool operator()(const T& left, const T& right)const
{
return left < right;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& left, const T& right)const
{
return left > right;
}
};
template <class T, class Contioner = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
priority_queue()
{}
template<class Iterator>
priority_queue(Iterator first, Iterator last)
: _con(first, last) // 1、使用vector的区间构造函数来初始化_con
{
// 2、建堆:从完全二叉树的最后一个非叶子结点来进行向下调整
for (int i = (size() - 2) / 2; i >= 0; i--)
{
AdjustDown(i);
}
}
void push(T val)
{
// 1、将代插入的元素进行尾部插入
_con.push_back(val);
// 2、将该元素进行向上调整
AdjustUp();
}
void pop()
{
if (empty())
return;
// 1、将堆顶元素与最后一个元素进行位置交换
std::swap(_con[0], _con[size() - 1]);
// 2、将换到最后一个位置的堆顶元素进行尾删
_con.pop_back();
// 3、将新的堆顶元素进行向下调整
AdjustDown(0);
}
size_t size()
{
return _con.size();
}
const T& top()const
{
return _con[0];
}
bool empty()const
{
return _con.empty();
}
private:
void AdjustDown(size_t parent)
{
size_t sz = size();
size_t child = 2 * parent + 1;
Compare com;
while (child < sz)
{
// 如果左孩子存在 且 左孩子小于右孩子 就将child进行更新
if (child + 1 < sz && com(_con[child], _con[child + 1]))
child += 1;
if (com(_con[parent], _con[child]))
{
std::swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void AdjustUp()
{
size_t child = size() - 1;
Compare com;
size_t parent = (child - 1) / 2;
while (child)
{
if (com(_con[parent], _con[child]))
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
return;
}
}
private:
Contioner _con;
};
}
void TestMyPriority_queue1()
{
int array[] = { 1,2,3,4,5,6,7,8,9 };
wei::priority_queue<int> q(array, array+sizeof(array)/sizeof(array[0]));
cout << q.size() << endl;
cout << q.top() << endl;
}
void TestMyPriority_queue2()
{
wei::priority_queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
q.push(6);
cout << q.size() << endl;
cout << q.top() << endl;
q.pop();
q.pop();
cout << q.size() << endl;
cout << q.top() << endl;
}
void TestMyPriority_queue3()
{
int array[] = { 1,2,3,4,5,6,7,8,9 };
wei::priority_queue<int, vector<int>, wei::Greater<int>> q(array, array + sizeof(array) / sizeof(array[0]));
cout << q.size() << endl;
cout << q.top() << endl;
}