本节前言:本节重点是通过栈和队列了解STL六大组件之“适配器”,以及通过了解优先级队列的底层原理学习STL六大组件之“仿函数”,这俩组件在C++进阶学习中将会发挥非常重要的作用。
一,栈的介绍
1.1 关于栈
1,队列我们在学习数据结构的时候已经了解过了,它是一种“先进后出,后进先出”的一种数据结构
2,但与C语言不同,STL将栈当作一种适配器来使用,专门用在具有后进先出操作的上下文环境中,只能从容器的一端进行元素的插入和删除,关于适配器下面会有详细讲解
3,stack是作为容器适配器被实现的,所以stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器支持以下操作:
①empty:判空操作
②back:获取尾部元素操作
③push_back:尾部插入元素操作
④pop_back:尾部删除元素操作
4,标准容器vector,list,depue均符合这些要求,默认情况下,如果没有未stack指定底层容器,将使用deque容器,关于deque容器后面有详细了解
1.2 栈的模拟实现
namespace my_stack
{
template<class T,class Container = deque<T>>
class Stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
T& top()
{
return _con.back();
}
bool empty()const
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
1.3 测试代码
void test_stack()
{
//my_stack::Stack<int,list<int>> st
my_stack::Stack<int,vector<int>> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
cout << endl;
}
二,队列的介绍
2.1 关于队列
1,在数据结构的学习中我们也学习过队列,它是一种”先进先出“的数据结构,专门用于在FIFO上下文中操作
2,队列也作为容器适配器实现,和stack一样
3,和stack一样,queue的底层容器也可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器支持以下操作:
①empty:检测队列是否为空
②size:返回队列中有效元素的个数
③front:返回对头元素的引用
④back:返回队尾元素的引用
⑤push_back:在队列尾部入数据
⑥pop_front:在队列头部入数据
2.2 队列模拟实现
namespace my_queue
{
template<class T, class Container = deque<T>>
class Queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
T& back()
{
return _con.back();
}
T& front()
{
return _con.front();
}
const T& back()const
{
return _con.back();
}
const T& front()const
{
return _con.front();
}
bool empty()const
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
2.3 测试代码
void test_queue()
{
my_queue::Queue<int,list<int>> q;
//不支持 vector没有pop_front
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
}
三,优先级队列(priority_queue)的介绍
3.1 关于pirority_queue
1,优先级队列被实现为容器适配器,queue提供一组特定的成员函数来访问其元素
2,优先级队列默认使用vector作为底层存储数据的容器,并且在vector上增加了堆算法来将数据构建成堆结构,所以笼统来说,priority_queue就是堆,因此,所有需要用到堆的位置都可以考虑用priority_queue,默认情况下priority_queue是大堆
3,底层容器可以是任何标准容器类模板,也可以是其他特定涉及的容器类,容器需要支持随机访问和迭代器访问,支持以下操作:
①empty:检测容器是否为空
②size返回容器中的有效元素个数
③front:返回容器中第一个元素的引用
④push_back:在容器尾部插入元素
⑤pop_back:删除容器尾部元素
3.2 仿函数
又叫函数对象,一般定义成一个类,作用是根据需要重载函数调用符号(),赋予其特定功能完成操作,重载的()的返回值也根据需要去给
下面是模拟实现priority_queue中判断堆左右孩子大小关系时用到的仿函数实现
namespace 阿巴阿巴
{
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;
}
};
}
接下来是仿函数的演示代码
int main()
{
阿巴阿巴::less<int> lsFunc;
cout << lsFunc(1, 2) << endl;//等价
cout << lsFunc.operator()(1, 2) << endl;
阿巴阿巴::greater<int> gtFunc;
cout << gtFunc(1, 2) << endl;//等价
cout << gtFunc.operator()(1, 2) << endl;
return 0;
}
3.3 模拟实现priority_queue
namespace my_priority_queue
{
template<class T,class Container = vector<T>,class Compare = std::less<T>>
class Priority_Queue //compare进行比较的仿函数 less -> 大堆
{
public:
//无参构造
Priority_Queue()
{}
//迭代器区间构造
template<class InputIterator>
Priority_Queue(InputIterator first,InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
//建堆
for (int i=(_con.size()-1-1)/2;i >= 0;i--)
{
adjust_down(i);
}
}
void adjust_up(size_t child)
{
Compare com;
size_t parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[child] > _con[parent])
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
std::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+1] > _con[child])
//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
{
++child;
}
//if (_con[child] > _con[parent])
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size() - 1);
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
bool empty() const
{
return _con.empty();
}
const T& top()
{
return _con[0];
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
3.4 测试代码
namespace 阿巴阿巴
{
template<class T>
class less
{
public:
bool operator()(const T& l, const T& r)const
{
return l < r;
}
};
template<class T>
class greater
{
public:
bool operator()(const T& l, const T& r)const
{
return l > r;
}
};
}
namespace my_priority_queue
{
template<class T,class Container = vector<T>,class Compare = 阿巴阿巴::less<T>>
class Priority_Queue //compare进行比较的仿函数 less -> 大堆
{
public:
//无参构造
Priority_Queue()
{}
//迭代器区间构造
template<class InputIterator>
Priority_Queue(InputIterator first,InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
//建堆
for (int i=(_con.size()-1-1)/2;i >= 0;i--)
{
adjust_down(i);
}
}
void adjust_up(size_t child)
{
Compare com;
size_t parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[child] > _con[parent])
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
std::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+1] > _con[child])
//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
{
++child;
}
//if (_con[child] > _con[parent])
//if (_con[parent] < _con[child])
if (com(_con[parent], _con[child]))
{
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size() - 1);
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
bool empty() const
{
return _con.empty();
}
const T& top()
{
return _con[0];
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
如果要改成大堆,需要将仿函数由less改为greater,并且将相关的if操作里的大小符号更改
注意:上面使用的是我们自己定义的仿函数,类似于C语言中的函数指针,如果我们想要比较自定义类型时,只要该类型支持重载了 “>” 和 “<”,就可以适用于优先级队列
另外一种情况:如果优先级队列存的时某个类型的地址,这个时候就不能用less和greater控制堆结构,因为需要比较指针指向值的优先级,所以这时候就需要我们自己实现对应的仿函数
四,deque的介绍
4.1 关于deque
那deque是如何借助其迭代器维护其假想连续的结构呢?
和链表访问一样,使用cur'进行++操作,每个buffer都有last,cur等于last时跳转到下一个buffer的first,反复执行,直到cur等于最后一个buffer的end,也就是nullptr的时候停下来
4.2 deque的缺陷