stack的简介以及使用
stack就是我们先前在数据结构里面使用过的栈这个数据结构,它仅仅允许在一段插入和删除数据,这一段被称作为栈顶。 同样,我们也通过官方文档来认识一下stack有什么接口可以使用:
接下来我们就通过一段简单的代码来看一看怎么使用stack
#include
#include
using namespace std;
void test()
{
std::stack<int>st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
int top = st.top();
cout << top << " ";
st.pop();
}
cout << endl;
}
int main()
{
test();
return 0;
}
关于stack的功能介绍就到这里,以后需要使用数据结构里面的stack我们就无需自己写了。
queue的简介和使用
queue是我们数据结构里面的队列,遵循先进先出的原则。 我们同样是通过官方文档来看一看queue的使用。
和stack不一样的是,queu可以取队头的数据的同时也可以取队尾的数据,但是我们都是取队头的数据。
#include
#include
using namespace std;
void test()
{
queue<int>q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
int font = q.front();
cout << font << " ";
q.pop();
}
cout << endl;
}
int main()
{
test();
return 0;
}
priority_queue的使用和介绍
实际上,除了普通的栈和队列以外。官方库还提供了优先级队列,优先级队列的行为就是数据结构里面的堆。下面我们来看一看优先级队列priority_queue怎么使用。一样,我们还是通过官方文档来看。
接下来我们也透过代码来看一看priority_queue怎么使用
#include
#include //priority_queue也在这个文件
using namespace std;
void test()
{
priority_queue<int>pq;
pq.push(4);
pq.push(6);
pq.push(2);
pq.push(8);
while (!pq.empty())
{
int top = pq.top();
cout << top << " ";
pq.pop();
}
cout << endl;
}
int main()
{
test();
return 0;
}
这里默认形成的是大堆,而如果要生成一个小堆,那么就要传递仿函数。
#include
#include
#include //官方库定义的仿函数所在的头文件
void test()
{
//这里的greator是一个仿函数
priority_queue<int,vector<int>,greater<int>>pq;
pq.push(4);
pq.push(6);
pq.push(2);
pq.push(8);
while (!pq.empty())
{
int top = pq.top();
cout << top << " ";
pq.pop();
}
cout << endl;
}
int main()
{
test();
return 0;
}
关于priority_queue的使用就介绍到这里,下面和我一起认识一下仿函数。
仿函数介绍
我们介绍一下什么是仿函数。首先我们回顾C语言的qsort,我们知道qsort用了第四个参数是函数指针。因为函数指针的存在,所以qsort可以根据函数指针指定的排序规则排序,但是函数指针的结构太复杂了!而且不符合面向对象程序的设计! 为了能够做到指定规则排序,所以C++设计了仿函数来代替函数指针的作用,而仿函数的本质就是一个operator()的类。
//设计一个仿函数。
//也可以用class,不过operator()就要声明成public的函数
template<class T>
struct Less
{
//重载()
bool operator()(const T& a,const T& b)const
{
return a<b;
}
};
官方库里面写好了less和greater两个仿函数,它们定义在functional头文件里面。
有的时候,我们需要自己定义仿函数,不过大多数场景下,官方库里面定义的这两个仿函数已经就够用了。
什么是适配器模式
仔细观察官方库里面的stack和queue的声明
template <class T, class Container = deque<T> > class stack;
template <class T, class Container = deque<T> > class queue;
我们看到,这里给了一个模板参数Container 其实严格意义上来说,stack和queue并不是容器,而是容器适配器。 既然是容器适配器,就要提到很重要的设计思想---->适配器模式。
所谓的适配器模式,就是借助类似"充电头"这样的转换器来帮助转换出我们想要的,而这里的充电头就是模板参数Container!以stack为例,如果这里的Container是vector,那么我们的栈就是数组栈,而如果是list,那么生成的就是链式栈! 借助适配器,我们无需关心stack使用什么数据结构实现。同理queue也是一样!
stack的模拟实现
接下来我们来模拟实现一下stack
#pragma once
#include
#include
#include
#include
using namespace std;
namespace chy
{
//stack是容器适配器,不独立实现,而是借用其他容器生成
template<typename T,typename 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();
}
const T& top()const
{
return _con.back();
}
bool empty() const
{
return _con.empty();
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
void test_st()
{
chy::stack<int>st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
cout << "namespace chy" << endl;
while (!st.empty())
{
int top = st.top();
cout << top << " ";
st.pop();
}
cout << endl;
}
int main()
{ test_st();
return 0;
}
这里的默认使用的是deque进行适配,deque是一个可以在头尾快速插入删除的双端队列,并且能够支持随机访问,但是其实是一个"外强中干"的数据结构,最后我们会介绍一下这个容器。
queue的模拟实现
接下来我们来模拟实现以下queue
#pragma once
#include
#include
#include
using namespace std;
namespace chy
{
template<typename T,typename Container=deque<T>>
class queue
{
//使用其他容器适配生成,不可以使用vector适配
//因为vector没有pop_front()成员
public:
void push(const T& val)
{
_con.push_back(val);
}
void pop()
{
_con.pop_front();
}
bool empty() const
{
return _con.empty();
}
T& front()
{
return _con.front();
}
T& back()
{
return _con.back();
}
const T& front()const
{
return _con.front();
}
const T& back() const
{
return _con.back();
}
private:
Container _con;
};
}
void test_queue1()
{
//使用list适配,不能用vector,因为vector没有pop_front()
chy::queue<int, list<int>>q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
cout << "namespace chy" << endl;
while (!q.empty())
{
int top = q.front();
cout << top << " ";
q.pop();
}
cout << endl;
}
int main()
{
test_queue1();
return 0;
}
priority_queue的模拟实现
最后就是优先级队列的模拟,相比于前面两个直接适配。优先级队列push要向上调整,而pop()的时候要向下调整,所以相对会复杂一点。
#pragma once
#include
#include
#include
#include
using namespace std;
namespace chy
{
//compare是一个仿函数
template<class T,class Container=vector<T>,class Compare=less<T>>
class priority_queue
{
public:
void push(const T& val)
{
_con.push_back(val);
_adjustup(_con.size() - 1);
}
void pop()
{
//删除交换堆顶,然后删除
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
_adjustdown(0);
}
const T& top()const
{
return _con[0];
}
bool empty()const
{
return _con.empty();
}
size_t size()const
{
return _con.size();
}
//堆需要调整,所以我们要提供调整算法
private:
void _adjustup(size_t child)
{
Compare func;
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (func(_con[parent],_con[child]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
//其他位置都处理好了
else
{
break;
}
}
}
void _adjustdown(size_t parent)
{
Compare func;
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && func(_con[child],_con[child+1]))
{
++child;
}
if (func(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
private:
Container _con;
};
}
void test_pq()
{
chy::priority_queue<int> pq;
pq.push(4);
pq.push(6);
pq.push(2);
pq.push(8);
cout << "namespace chy" << endl;
while (!pq.empty())
{
int top = pq.top();
cout << top << " ";
pq.pop();
}
cout << endl;
}
void test_pq1()
{
chy::priority_queue<int, vector<int>, greater<int>> pq;
pq.push(4);
pq.push(6);
pq.push(2);
pq.push(8);
cout << "namespace chy" << endl;
while (!pq.empty())
{
int top = pq.top();
cout << top << " ";
pq.pop();
}
cout << endl;
}
int main()
{
test_pq();
test_pq1();
//test();
return 0;
}
外强中干的deque
可能细心的你已经注意到了,stack和queue的默认适配容器都是deque,那么这个deque究竟是何许神仙呢?其实这个deque并没有什么特别神奇的地方,它的结构非常复杂,下面是一个deque的结构图
其实,deque本质上划分了一块一块很小的内存,接着使用了1个指针数组来记录每一个小内存块的地址(为了支持随机访问)。但其实真实随机访问的效率比vector慢非常多!所以看似有着vector和list的所有优点,但是实际上并不是特别实用。但是deque的优点特别适合stack和queue,所以stack和queue都是默认使用deque来适配
以上就是本篇文章的全部内容,如果有不足或错误之处还望指出。希望大家共同进步。