STL —— stack、queue

图片名称

 
博主首页: 有趣的中国人
 
专栏首页: C++专栏
 

目录

 1. 容器适配器

2. 栈的模拟实现

3. 队列的模拟实现

4. 双端队列deque 

4.1 deque的原理介绍

 4.2 deque的缺陷

4.3 为什么选择deque作为stack和queue的底层默认容器


本篇文章主要讲解  stack 和 queue  的相关内容

 std::stack 是一个模板类,定义在 <stack> 头文件中,它基于其他的底层容器来实现栈的功能。默认情况下,std::stack 使用 std::deque 作为其底层容器,但也可以使用其他 STL 容器,如 std::vectorstd::list

基本操作

  1. 压入元素: 使用 push() 成员函数将元素推入栈顶。

  2. 弹出元素: 使用 pop() 成员函数从栈顶移除元素。

  3. 访问栈顶元素: 使用 top() 成员函数获取栈顶元素的引用,但不会将其从栈中移除。

  4. 判断栈是否为空: 使用 empty() 成员函数检查栈是否为空。

  5. 获取栈的大小: 使用 size() 成员函数获取栈中元素的数量。

std::queue 是一个模板类,定义在 <queue> 头文件中,它基于其他的底层容器来实现队列的功能。默认情况下,std::queue使用 std::queue 作为其底层容器,但也可以使用其他 STL 容器,如 std::list

基本操作

  1. 入队操作: 使用 push() 成员函数将元素推入队列的末尾。

  2. 出队操作: 使用 pop() 成员函数从队列的头部移除元素。

  3. 访问队列头部元素: 使用 front() 成员函数获取队列头部元素的引用,但不会将其从队列中移除。

  4. 访问队列尾部元素: 使用 back() 成员函数获取队列尾部元素的引用,但不会将其从队列中移除。

  5. 判断队列是否为空: 使用 empty() 成员函数检查队列是否为空。

  6. 获取队列的大小: 使用 size() 成员函数获取队列中元素的数量。


 1. 容器适配器

如果按照正常的思路实现stack,那么其中的元素应该有:

class stack
{

private:
	int _top;
	int _size;
	int _capacity;
};

但是,在C++中,用了适配器的模式,何为适配器呢?

我们知道stack模拟实现的过程与vector类似,那我们能否用vector来实现stack呢?

肯定是可以的,容器适配器是一种设计模式,它提供了一种简单的方式来修改或扩展现有容器的接口,以满足特定的需求或限制。


2. 栈的模拟实现

我们在实现栈的时候就可以用到容器适配器的模式,只需要在传模板参数的时候多加一个参数即可:

template<class T, class Container = vector<T>>
class stack
{
public:
	void push(const T& val)
	{
		_con.push_back(val);
	}
	void pop()
	{
		_con.pop_back();
	}
	size_t size() const
	{
		return _con.size();
	}
	bool empty()
	{
		return _con.empty();
	}
	T& top()
	{
		return _con.back();
	}
private:
	Container _con;
};
void stack_test1()
{
	stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);
	st.push(5);
	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
	cout << endl;
}


3. 队列的模拟实现

类似的,我们可以用容器适配器来模拟实现队列:

template<class T, class Container = vector<T>>
class stack
{
public:
	void push(const T& val)
	{
		_con.push_back(val);
	}
	void pop()
	{
		_con.pop_back();
	}
	size_t size() const
	{
		return _con.size();
	}
	bool empty()
	{
		return _con.empty();
	}
	T& top()
	{
		return _con.back();
	}
private:
	Container _con;
};
void stack_test1()
{
	stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);
	st.push(5);
	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
	cout << endl;
}


4. 双端队列deque 

4.1 deque的原理介绍

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
 

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:


双端队列底层是一段假想的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:


那deque是如何借助其迭代器维护其假想连续的结构呢?


 4.2 deque的缺陷

vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。


list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。


但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vectorlistdeque的应用并不多,而目前能看到的一个应用就是,STL用其作为stackqueue的底层数据结构

4.3 为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性结构,都可以作为stack的底层容器,比如vectorlist都可以;queue是先进先出的特殊线性数据结构,只要具有push_backpop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. stack中元素增长时,dequevector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷.。

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值