🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻强烈推荐优质专栏: 🍔🍟🌯C++的世界(持续更新中)
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解容器适配器 "栈"的使用以及模拟实现.
金句分享:
✨那些所谓的遗憾,可以也是一种成长!✨
前言
目录
- 前言
- 一、"栈"的介绍:
- 二、栈的使用
- 2.1 创建栈
- 2.2 栈的常见使用接口
- 🍔push()和emplace()的区别
- 2.3 练练手
- 🍉用栈实现队列
- 三、模拟实现
- 四、结语
一、"栈"的介绍:
栈是一种先进后出(Last-In-First-Out)的数据结构,类似于往箱子里面放东西,最先放进去的在最下面,只有上面的东西都被拿出来,才能取到下面的.
栈有两个基本操作:压入(push
)和弹出(pop
)。新元素通过压入操作被添加到栈的顶部,而位于栈顶的元素可以通过弹出操作被移除。除此之外,栈还有一个重要的操作是访问栈顶的元素,这可以通过 top
() 方法实现。
-
stack
是一种容器适配器,适用于先进后出的环境中.
何为容器适配器?就是将容器进行一定封装形成的一个只在容器一端进行元素的插入与获取,以及删除操作。栈本身它是一种容器类型,但是它是基于其他容器类型的实现而创建的。 -
stack
是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。 -
stack
的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下
操作:
empty
:判断是否为空栈
top
:获取栈顶元素
push_back
:压栈
pop_back
:出栈 -
标准容器
vector
、deque
、list
均符合这些需求,默认情况下,如果没有为stack
指定特定的底层容器,
默认情况下使用deque
。
二、栈的使用
接口名 | 解释 |
---|---|
empty() | 判断是否为空栈 |
size() | 返回栈中有效元素的个数 |
top() | 返回栈顶元素的引用(方便修改) |
push() | 将新元素压栈 |
emplace() | 将新元素压栈 |
pop() | 将栈顶元素出栈 |
2.1 创建栈
stack<int> s1;
stack<string> s2;
stack<double> s3;
2.2 栈的常见使用接口
示例代码:
void test1()
{
//创建一个存储整形的栈
stack<int> s1;
//插入数据
s1.push(5);
s1.push(4);
s1.push(3);
s1.push(2);
s1.push(1);
s1.emplace(-1);
//查看栈中有效元素的个数
cout << "size=" << s1.size() << endl;
修改栈顶元素
//int& top = s1.top();
//top = 66;
//打印栈中的所有元素
while (!s1.empty())
{
cout << s1.top() << endl;//打印栈顶元素
s1.pop();//出栈
}
}
运行结果:
size=6
-1
1
2
3
4
5
🍔push()和emplace()的区别
在STL
中,push
()和emplace
()都是向容器中添加一个元素,但是它们有以下不同:
1.① push
()只能将一个已经创建的对象压入容器中,并且该对象的副本被创建,因此需要调用拷贝构造函数。
②而emplace
()直接在容器中创建一个新对象,因此不需要调用拷贝构造函数创建副本.
-
push
()方法需要传递一个已经创建好的对象,而emplace
()方法需要传递的是要创建对象的构造函数参数。 -
push
()方法适用于已经存在的对象,而emplace
()方法适用于构造一个新对象。
从上面来看emplace
()方法更高效,因为它直接在容器中创建一个新对象,而不是创建一个临时对象并将其复制到容器中。因此,在需要创建大量对象的情况下,emplace
()方法比push
()方法更适合。
class People
{
friend ostream& operator<<(ostream& _cout, const People& p1);
public:
People(string name, int age = 18)
:_name(name)
,_age(age)
{
cout << "People的构造:" << endl;;
}
People(const People& p1)
:_name (p1._name)
,_age(p1._age)
{
cout << "People的拷贝构造:" << endl;
}
private:
string _name;
int _age;
};
ostream& operator<<(ostream& _cout,const People& p1)
{
_cout << p1._name;
_cout << " :";
_cout << p1._age;
return _cout;
}
void test1()
{
stack<People> s1;
People p1("张三");
s1.push(p1);
//s1.push("王五",22);//报错
s1.emplace("李四",20);
while(!s1.empty())
{
cout << s1.top() << endl;
s1.pop();
}
}
int main()
{
test1();
return 0;
}
People的构造:
People的拷贝构造:
People的构造:
李四 :20
张三 :18
首先:
push
不能像emplace("李四",20);
这样直接传参构造对象.
其次:
push
还会调用拷贝构造函数,将对象的副本压栈.
2.3 练练手
🍉用栈实现队列
还记得我们c语言阶段实现的这个题目吗?
用栈实现队列博文(C语言版本)
题目链接:传送门
由于C
语言中并没有STL
库,需要手撕栈.
当时写了一百多行代码,才完成这道题.
如今,C++
不仅不用手动造轮子,还会自动调用析构和构造函数.
试着练练手吧!
参考代码:
class MyQueue {
public:
MyQueue() {}
void push(int x) {
push_stack.push(x);
}
int pop() {
int ret = peek();//保存队首元素
pop_stack.pop();//出队列
return ret;
}
int peek() {//返回队首元素
if (pop_stack.empty()){
//倒数据
while (!push_stack.empty()){
pop_stack.push(push_stack.top());
push_stack.pop();
}
}
int ret = pop_stack.top();
return ret;
}
bool empty() {
return push_stack.empty() && pop_stack.empty();
}
private:
stack<int> push_stack;
stack<int> pop_stack;
};
三、模拟实现
其实stack
的模拟实现极其简单.
这里底层复用vector
的接口即可.
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
namespace cjn
{
template<class T>
class stack
{
public:
stack() {}
void push(const T& x) {
_c.push_back(x);
}
void pop() {
_c.pop_back();
}
T& top() {
return _c.back();
}
const T& top()const {
return _c.back();
}
size_t size()const {
return _c.size();
}
bool empty()const {
return _c.empty();
}
private:
std::vector<T> _c;//底层采用vector的接口实现
};
}
四、结语
栈
的应用也是十分广泛的,后续的二叉树,表达式求值都利用了栈的后进先出的特性.