优先级队列,仿函数,反向迭代器
- 优先级队列
- 认识优先级队列
- 模拟实现优先级队列
- 浅谈仿函数
- 仿函数的大致了解
- 仿函数的实现
- 反向迭代器
- 什么是反向迭代器?
- 反向迭代器的实现
- 结语
优先级队列
认识优先级队列
优先级队列(priority_queue)不是队列。优先级队列是一种容器适配器,与栈(stack),队列(queue)具有类似的功能。
先来了解一下优先级队列有哪些功能。看下图:
优先级队列底层是一个堆(默认是大堆),第二个参数默认给的是vector,不适合list,第三个参数是仿函数(函数对象)。
模拟实现优先级队列
大部分成员函数可以通过调用vector的成员函数来实现。
#pragma oncenamespace bit {//目前建堆默认大堆,如果想建小堆怎么办?template<class T, class Container = vector<T>>class priority_queue {private://不想让别人知道这两个函数,可以设置为私有void AdjustUp(int child){//向上调整需要和兄弟比较吗?int parent = (child - 1) / 2;while (child > 0){if (_con[child] > _con[parent]){std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}//向下调整删除数据调用void AdjustDown(int parent){Compare com;int child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child + 1] > _con[child]){++child;}if (com(_con[child], _con[parent])){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}public:template<class InputIterator>priority_queue(InputIterator first, InputIterator last){//传过来的迭代器可能不是有序的,所以要排序建堆。while (first != last){push(*first);++first;}}priority_queue()//会调用vector的默认构造函数{}void push(const T& val){_con.push_back(val);AdjustUp(_con.size() - 1);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}void pop(){//为什么swap不交换swap(_con[0], _con[_con.size() - 1]);//_con.size() - 1;//为什么这个程序不执行?执行了,但是size没变,只是_con.size() - 1_con.pop_back();AdjustDown(0);}void swap(priority_queue& pq){std::swap(_con, pq._con);}size_t size() const{return _con.size();}private:Container _con;};
}
如果想建小堆,但不增加代码量的冗余,有办法吗?
浅谈仿函数
仿函数的大致了解
怎么建小堆?可以增加一个参数函数指针,这个函数来控制大堆还是小堆。但是函数指针声明比较复杂,有一些声明很长,导致通用性变差。所以,仿函数就来了。
写一个类,类中写相应的方法,这个类作为参数,那么就可以调用该类中的方法了。
仿函数(函数对象)不是函数调用,只是具有函数的功能。
如下代码是对上面代码的补充与修改
仿函数的实现
#pragma oncenamespace bit {template<class T, class Container = vector<T>, class Compare = Greater<int>>class priority_queue {private://不想让别人知道这两个函数void AdjustUp(int child){Compare com;int parent = (child - 1) / 2;while (child > 0){if (com(_con[child], _con[parent])){std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void AdjustDown(int parent){Compare com;int child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child + 1], _con[child])){++child;}if (com(_con[child], _con[parent])){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}public:template<class InputIterator>priority_queue(InputIterator first, InputIterator last){//传过来的迭代器可能不是有序的,所以要排序建堆。while (first != last){push(*first);++first;}}priority_queue()//会调用默认的构造函数{}void push(const T& val){_con.push_back(val);AdjustUp(_con.size() - 1);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}void pop(){//为什么swap不交换swap(_con[0], _con[_con.size() - 1]);//_con.size() - 1;//为什么这个程序不执行?执行了,但是size没变,只是_con.size() - 1_con.pop_back();AdjustDown(0);}void swap(priority_queue& pq){std::swap(_con, pq._con);}size_t size() const{return _con.size();}private:Container _con;};
}
template<class T>
struct Less
{
public:bool operator()(T& x, T& y){return x > y;}
};
template<class T>
struct Greater
{
public:bool operator()(T& x, T& y){return x < y;}
};
仿函数很方便,类中写许多的函数,资源的管理性不错,参数就可以只传一个类,封装性很强,灵活性很高。
反向迭代器
什么是反向迭代器?
**反向迭代器(reverse_iterator)**就是迭代器的相反。rbegin就是end,rend就是begin。
图中表示的rbegin,rend就是反向迭代器。
那如何实现反向迭代器呢?
反向迭代器的实现
仔细观察上面那张图,begin与rbegin,end与rend都是对称的。
·反向迭代器初始化可以调用正向迭代器的初始化
·反向迭代器++就是正向迭代器 的–,–就是正向迭代器的++。
先来看看库里面的反向迭代器是如何实现的。
反向迭代器中有一个正向迭代器成员。++和–操作没有问题,都是调用正向迭代器的++和–函数,但是解引用(*)函数中为什么要–?
我们根据如下代码进行分析:
#include<iostream>
#include<vector>
using namespace std;int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::reverse_iterator rit = v.rbegin();for (rit != v.rend()){cout << *rit << " ";++rit;}cout << endl;
}
)rbegin 是由 end 初始化的,所以指向最后一个元素的下一个位置。所以解引用时,rbegin 要到前一个位置,返回前一个位置的元素,rbegin不变,rbegin 和 rend 相等时循环结束。
反向迭代器的实现:
//反向迭代器可以封装一个正向迭代器,各种操作可以调用正向迭代器的函数
template<class Iterator, class Ref, class Ptr>
class ReverseIterator {
private:Iterator _it;
public:typedef ReverseIterator<Iterator, Ref, Ptr> Self;ReverseIterator()//调用正向迭代器的构造函数{}ReverseIterator(const Iterator& it):_it(it)//调用正向迭代器的拷贝构造函数{}Ref operator*()//解引用指向前一个位置的元素,本身不变。{Iterator tmp = _it;return *(--tmp);}Self& operator++()//调用正向迭代器--函数{--_it;return *this;}Self& operator--()//调用正向迭代器++函数{++_it;return *this;}bool operator!=(const Self& s)//调用正向迭代器的!=函数{return _it != s._it;}
};
结语
优先级队列(priority_queue)实现较为简单,与数据结构中的堆很相似。实现优先级队列与实现栈和队列的区别是:栈和队列可以直接复用其他容器的函数,优先级队列则需要自己加一些东西进去加工,如向下调整,仿函数等。
仿函数替换成函数指针。函数指针很麻烦,写个函数声明很长,读懂也容易绕晕。仿函数就是一个类,类中可以写许多函数,封装的很好,使用时很方便。
反向迭代器(reverse_iterator)就是理解解引用(*)的过程,写起来就是封装一个正向迭代器。