文章目录
- 优先级队列
- priority_queue的模拟实现
- 框架
- 无参的构造(默认构造)
- 迭代器区间构造
- 向上调整
- 向下调整
- 插入
- 删除
- 取堆顶的数据
- 求数据个数
- 验满
- 初识仿函数
- 模拟实现
- 仿函数更改后的向上调整
- 仿函数更改后的向下调整
- 反向迭代器
- 具体实现
优先级队列
1.优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2.此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
3.优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
4.标准容器类vector和deque满足这些需求。默认情况下,如果没有特定的priority_queue类实例化指定容器类,则使用vector。
5.实际数据类型是数组
priority_queue的模拟实现
默认情况下,priority_queue是大堆
框架
template<class T,class Container = vector<T>>
class class Priority_Queue
{
public:
//实现
......
private:
Container _con;
无参的构造(默认构造)
Priority_Queue()//会调用适配器的默认构造
{}
迭代器区间构造
template<class InputIterator>
Priority_Queue(InputIterator first, InputIterator last)
:_con(first,last)
{
//向下建堆
for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
{
adjust_down(i);//向下调整
}
}
向上调整
void adjust_up(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (_con[child] > _con[parent])//建大堆
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
向下调整
void adjust_down(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child + 1] > _con[child])//大堆
{
child++;
}
if (_con[child]> _con[parent])//大堆
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
插入
void push(const T& val)
{
_con.push_back(val);
adjust_up(_con.size() - 1);//把新插入的儿子的下标传过去,然后向上调整
}
删除
void pop()
{
swap(_con[0], _con[_con.size() - 1]);//头删-先和最后的儿子交换,然后再尾删
_con.pop_back();
adjust_down(0);//向下调整
}
取堆顶的数据
const T& top()const
{
return _con[0];
}
求数据个数
size_t size() const
{
return _con.size();
}
验满
bool empty()const
{
return _con.empty();
}
初识仿函数
通过查阅优先级队列的大堆小堆的实现,大于和小于的地方使用的是仿函数里面的比较。仿函数引用的头文件是 #include< functional >
模拟实现
template<class T>
class less//大根堆
{
public:
bool operator()(const T& x,const T& y)const
{
return x < y;//后一个是否小于前一个
}
};
template<class T>
class greater//小根堆
{
public:
bool operator()(const T& x, const T& y)const
{
return x > y;//后一个是否大于前一个
}
};
1.这里的仿函数的大于小于的比较由仿函数的实现决定,而底层不会暴露出来,更安全。
2.仿函数针对的是泛型,用法更广泛。比如比较的是内嵌类型,那么天然的>或者<可能可以满足,而对应自定义类型的比较,就需要自己去实现然后封装起来。
比如用在冒泡排序,以往的冒泡排序的实现,具体的升序和降序由模拟实现中的>或<号决定,而这符号等于暴露了底层实现。那么这里可以通过仿函数隐藏起来,不保留底层实现。
仿函数更改后的向上调整
void adjust_up(size_t child)
{
Compare Com;
size_t parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[parent]<_con[child])//建大堆
if(Com(_con[parent],_con[child]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
仿函数更改后的向下调整
void adjust_down(size_t parent)
{
Compare Com;
size_t child = parent * 2 + 1;
while (child < _con.size())
{
//if (child + 1 < _con.size() && _con[child] < _con[child+1])//大堆
if(child+1<_con.size()&&Com(_con[child],_con[child+1]))
{
child++;
}
//if (_con[parent]> _con[child])//大堆
if(Com(_con[parent],_con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
反向迭代器
反向迭代器和迭代器的起始是对称的,但反向迭代器在解引用的时候有所不同,取数据时先反向迭代器–再解引用。
具体实现
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
public:
ReverseIterator(Iterator it)
:_it(it)
{}
Ref operator*()
{
Iterator tmp = _it;
return *(--tmp);
}
Ptr operator->()
{
return &(operator*());
}
Self& operator++()//前置++
{
--_it;
return *this;
}
Self& operator--()//前置--
{
++_it;
return *this;
}
Self operator++(int)//后置++
{
Iterator tmp = _it;
++_it;
return tmp;
}
Self operator--(int)//后置--
{
Iterator tmp = _it;
--_it;
return tmp;
}
bool operator!= (const Self& s) const
{
return _it != s._it;
}
bool operator==(const Self& s)const
{
return _it == s._it;
}
private:
Iterator _it;
};
list里面对反向迭代器的适配