118.C++ 中的stack
118.C++ 中的stack
1.stack的概念
先进后出的线性表。
栈是一个STL中的容器适配器,在std命名空间中,它限制插入和删除都在一个位置上(栈顶上),底层是deque(双端队列)。
要使用stack,需要添加头文件
#include <stack>
栈顶:进行数据插入和删除操作的一端
栈底:栈顶的另一端
入栈:栈的插入操作,也叫做压栈/进栈,入数据在栈顶
出栈:栈的删除操作,出数据也在栈顶
2.stack的构造
stack<typename> name;
构造函数 | 功能 |
---|---|
explicit stack (const container_type& ctnr); | 构造一个stack,其内部容器被初始化为ctnr的副本。 |
explicit stack (container_type&& ctnr = container_type()); | 构造一个stack,其内部容器通过移动构造来获取ctnr的值。 |
template <class Alloc> explicit stack (const Alloc& alloc); | 构造一个stack,其内部容器使用alloc作为参数构造。 |
template <class Alloc> stack (const container_type& ctnr, const Alloc& alloc); | 构造一个stack,其内部容器使用cntr和alloc作为参数构造。 |
template <class Alloc> stack (container_type&& ctnr, const Alloc& alloc); | 同上,移动构造 |
template <class Alloc> stack (const stack& x, const Alloc& alloc); | 构造一个stack,其内部容器使用x的内部容器作为第一个参数,而alloc作为第二个来构造。 |
template <class Alloc> stack (stack&& x, const Alloc& alloc); | 同上,移动构造 |
例子:
#include<iostream>
#include<stack>
#include<list>
#include<deque>
using namespace std;
int main()
{
deque<int> mydeck(3, 100); //创建一个deque含3个100
list<int> mylist(2, 200); //创建一个list含2个200
stack<int> first; //创建一个空的堆栈
stack<int> second(mydeck); //用mydeck的拷贝作为堆栈的初始值
//第二个参数模板默认即为 deque,因此可以不写。
stack<int, list<int>> third; //空堆栈,列表作为底层容器
stack<int, list<int>> fourth(mylist); //用mylist的拷贝作为堆栈的初始值
cout << "size of first: " << first.size() << endl;
cout << "size of second: " << second.size() << endl;
cout << "size of third: " << third.size() << endl;
cout << "size of fourth: " << fourth.size() << endl;
return 0;
}
输出:
size of first: 0
size of second: 3
size of third: 0
size of fourth: 2
3.stack容器内元素的访问
由于栈(stack)本身就是一种后进先出的数据结构,在STL中的stack只能通过top()
来访问栈顶元素
向栈中插入一个元素:s.push(x);
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
for (int i = 1; i <= 5; i++) s.push(i);
cout << s.top();
return 0;
}
4.stack中的函数
4.1push()
s.push(x);
把x入栈,时间复杂度为O(1),实例见stack容器内元素的访问
4.2top()
s.top();
获得栈顶元素,时间复杂度为O(1),实例见stack容器内元素的访问
4.3pop()
s.pop();
用来弹出栈顶元素,时间复杂度为O(1)
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
for (int i = 1; i <= 5; i++) s.push(i);
for (int i = 1; i <= 3; i++) s.pop();
//把 5 4 3 依次弹出
cout << s.top();
return 0;
}
输出:
2
4.4empty()
s.empty();
可以检测stack
内是否为空,返回true
为空,返回false
是非空,时间复杂度为O(1)
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
if (s.empty()) cout << "EMPTY" << endl;
else cout << "NOT EMPTY" << endl;
for (int i = 1; i <= 5; i++) s.push(i);
if (s.empty()) cout << "EMPTY" << endl;
else cout << "NOT EMPTY" << endl;
return 0;
}
输出:
EMPTY
NOT EMPTY
4.5size()
s.size();
返回stack
内元素的个数,时间复杂度为O(1)
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
for (int i = 1; i <= 5; i++) s.push(i);
cout << s.size();
return 0;
}
输出:
5
4.6empty():判断栈是否为空,为空返回true,否则返回false。
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
if(s.empty())
cout << "s为空";
else
cout << "s不为空" << endl;
return 0;
}
输出:
s为空
5.顺序栈
栈的顺序存储:同一般线性表的顺序存储结构完全相同。利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
●附设 top 指针,指示栈顶元素在顺序栈中的位置。
●另设 base 指针,指示栈底元素在顺序栈中的位置。但是,为了方便操作,通常top指示真正的 栈顶元素之上 的下标地址。
●另外,用stacksize表示栈可使用的最大容量。
使用数组作为顺序栈存储方式的特点:
简单、方便、但容易产生溢出(数组大小固定)。
上溢(overflow):栈已经满,又要压入元素。是一种错误,使问题处理无法进行。
下溢(underflow):栈已经空,还要弹出元素。一般不认为是错误,而是一种结束条件,即问题处理结束。
5.1顺序栈的定义
#define MAXSIZE 100
typedef struct
{
SElemType* base; //栈底指针
SElemType* top; //栈顶指针
int stacksize; //栈的最大容量
} SqStack;
但是顺序栈的top和base指针可以是 int 类型用来存放数组下标,并不是真正意义上的指针变量。
存数组单元的地址也可以,在做一些操作时(比如top-base,得到的值是两个数组单元的距离),和存放下标的方式区别不大。
5.2顺序栈的初始化
算法思路:
1.开辟一片大小为stacksize的空间
2.top和base都指向栈底
算法描述:
Status InitStack(SqStack& S)//构造一个空栈
{
S.base = new SElemType[MAXSIEZE];
//或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SElemType));
if (!S.base) exit(OVERFLOW); //存储分配失败
S.top = S.base; //栈顶指针等于栈底指针
S.stacksize = MAXSIZE;
return OK;
}
5.3顺序栈的销毁
算法思路:
-
把整个栈空间释放掉
1.释放掉base指向的空间
2.把stacksize设置为0
算法描述:
Status DestroyStack(SqStack& S)
{
if (S.base)
{
delete S.base;
S.stacksize = 0;
S.base = S.top = NULL;
}
return OK;
}
5.4判断顺序栈是否为空
算法思路:
- top指针和base指针都指向栈底,此时top == base
算法描述:
Status StackEmpty(SqStack S)
{
//若栈为空,返回TRUE,否则返回FALSE
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
5.5求顺序栈的长度
算法思路:
- 指针相减,差为两个指针的间隔,也就是元素个数。
算法描述:
int StackLength(SqStack S)
{
return S.top - S.base;
}
5.6取栈顶元素
算法思路:
1.判断栈是否存在并且非空
2.返回栈顶元素
算法描述:
Status GetTop(S, &e)
{
if (S.top == S.base) return ERROR;
e = *(S.top - 1);
return OK;
}
5.7清空顺序栈
算法思路:
●把栈当作是空就行了,也就是top也指向栈底位置。
算法描述:
Status ClearStack(SqStack S)
{
if (S.base) S.top = S.base;
return OK;
}
5.8顺序栈的入栈
算法思路
1.判断是否栈满,若满则出错(上溢)
2.将元素存到top所指的位置
3.top上移
算法描述:
Status Push(SqStack& S, SElemType e)
{
if (S.top - S.base == S.stacksize) //栈满
return ERROR;
*S.top++ = e; //(*S.top=e;S.top++;)
return OK;
}
5.9顺序栈出栈
算法思路:
1.判断是否栈空,若空则出错(下溢)
2.top下移
3.取top指针所指位置上的元素
算法描述:
Status Pop(SqStack& S, SElemType& e)
{
//若栈不空,则“删除”栈顶元素,用e返回,“删除”成功返回OK,失败返回ERROR
if (S.top == S.base) //if(StackEmpty(S))
return ERROR;
e = *--S.top; //--S.top;e = *S.top;
return OK;
}
6.链栈
逻辑都是相似的。
●链栈是运算受限的单链表,只能在链表头部进行操作。
几个注意的点:
●链表的头指针就是栈顶
●不需要头结点
●基本不存在栈满的情况
●空栈相当于头指针指向空
●插入和删除仅在栈顶处执行
6.1链栈的定义
typedef struct StackNode
{
SElemType data;
struct StackNode* next;
} StackNode, * LinkStack;
LinkStack S;
如同定义单链表。
6.2链栈的初始化
算法描述:
void InitStack(LinkStack& S)
{
//构造一个空栈,栈顶指针置空
S = NULL;
return OK;
}
6.3判断链栈是否为空
算法思路:
●头指针不指向任何元素时为空
算法描述:
Status StackEmpty(LinkStack S)
{
if (S == NULL) return TRUE;
else return FALSE;
}
6.4取栈顶元素
算法思路:
- 直接取出S所指的元素
算法描述:
SElemType GetTop(LinkStack S)
{
if (S != NULL)
return S->data;
}
6.5链栈的入栈
算法思路:
1.生成新结点p
2.将新节点插入栈顶
3.修改栈顶指针
算法描述:
Status Push(LinkStack& S, SElemType e)
{
p = new Stacknode; //生成新结点p
p->data = e; //将新节点的数据域置为e
p->next = S; //将新节点插入栈顶
S = p; //修改栈顶指针
return OK;
}
6.6链栈的出栈
算法思路:
1.判断栈是否存在且非空
2.保存当前栈顶地址
3.修改栈顶指针
4.释放刚才的栈顶空间
算法描述:
Status Pop(LinkStack& S, SElemType& e)
{
if (S == NULL) return ERROR;
e = S->data;
p = S;
S = S->next;
delete P;
return OK;
}
7.栈与一般线性表的区别
一般线性表 | 栈 | |
---|---|---|
逻辑结构 | 一对一 | 一对一 |
存储结构 | 顺序表、链表 | 顺序栈、链栈 |
运算规则 | 随机存取 | 后进先出(LIFO) |
参考: