栈
与前面的线性表相似,栈作为一种新的数据结构,也是用来保存数据的,同时栈可以使用顺序表和链表来进行实现。但是栈与线性表不同的是,栈是限定仅在表尾进行插入和删除操作的线性表。
栈是一种特殊的线性表,可以将它想象成一个薯片筒,只有一个开口。在栈中,将可以放入和拿出数据的一端称为栈顶,而另一端被称为栈底。由于栈依旧是线性表,所以栈中的元素也有前驱和后继的关系。
根据上述对于栈特点的介绍,可以知道栈在线性表的基础上,限定了插入的位置和删除的位置,只能在栈顶进行插入和删除。先插入的数据被压在更接近栈底的位置,后插入的数据被压在更接近栈顶的位置。故可以知道先删除的元素是最后插入的元素,即“先压入,后弹出”或**“后进先出”**。
数组栈Stack
接下来使用顺序表,也就是简单的一维数组来实现栈的功能。首先也是需要一个一维数组来表示栈的数据域,其次需要一个栈顶指针用于指向目前的栈顶。故数据结构如下所示。
#include <iostream>
#include <stdio.h>
#include <stack>
using namespace std;
#define MAX_SIZE 20 // 栈的最大容量
typedef int Element; // 元素重定义
typedef struct Stack // 数组栈
{
Element data[MAX_SIZE]; // 数据域
int top; // 栈顶指针
Stack()
{
for (int i = 0; i < MAX_SIZE; i++) // 初始化数据域为0
data[i] = 0;
top = -1; // 栈底初始化为-1
}
}Stack;
如上述代码所示,在构造函数中,将栈中所有元素初始化为0,并默认当前栈是一个空栈,即
t
o
p
=
−
1
top=-1
top=−1。栈的各种情况以及对应的栈顶指针的情况如下所示。
压入push
之后对于栈中的压入操作,也就是线性表中的插入操作。由于栈的特殊性,只能从栈顶进行插入,而栈顶永远指向最后一个压入的元素,所以在压入元素的时候,需要先将栈顶指针进行移动,指向一个空的区域,之后再将新元素放入即可。
代码实现就很简单,如下所示。
// 压入元素
bool Push(Stack* s, Element element)
{
if (s->top + 1 < MAX_SIZE) // 栈未满则加入
{
s->top++; // 移动栈指针
s->data[s->top] = element; // 加入新元素
return true;
}
return false; // 栈满
}
弹出pop
对于栈中的弹出操作pop,其实就是线性表中的删除,但是同样由于栈的特殊性,只能从栈顶弹出。同时考虑到这是一个数组栈,不能释放空间,故只能以移动栈指针的方法来实现元素的删除,根据前面的分析,栈指针减一即可。
// 弹出元素
bool Pop(Stack* s)
{
if (s->top >= 0) // 栈不空才可弹出元素
{
s->top--; // 栈顶指针下移即可
return true;
}
return false; // 栈空
}
获取栈顶元素top
由于栈的特殊性,每次也就只能获取栈顶元素的值,而不能获取中间元素的值,如果想获取中间元素的值,需要先将上面的元素移开,将想观察的元素移动到栈顶即可。
// 获取栈顶元素
Element Top(Stack* s)
{
if (s->top >= 0) // 要确保里面有元素
return s->data[s->top];
return -1;
}
判断是否为空isEmpty
在之后的一些操作中,例如使用栈来实现递归操作,需要判断栈是否为空,因为栈空时是不能继续访问栈顶元素的,这里也是使用栈指针来进行判断。
// 判断栈是否为空
bool isEmpty(Stack *s)
{
return s->top == -1;
}
返回栈的元素个数size
接下来补充一个功能,也就是获取栈中已经压入的元素的个数,根据前面的图解结合栈指针即可简单计算得到。
// 获取栈的容量
int Size(Stack* s)
{
return s->top + 1;
}
链表栈LinkStack
链表栈就是以链表作为数据域的数据存放方式,来实现栈的基础功能。在前面对于栈的功能有了一个简单的理解之后,可以得知在使用链表时,是可以不需要头结点的,只需要使用一个头指针即可,同时也可以使用该头指针来代替整个栈的栈顶指针。
而根据栈“先进后出”的特点,可以使用链表中的头插法,每次都从头指针位置处进行插入,删除时也是只需要删除头指针指向的结点即可。
压入push
压入部分参考链表中的头插法即可。
// 压入元素push
bool LinkPush(LinkStack* s, Element element)
{
if (s->size + 1 == MAX_SIZE) // 栈满
return false; // 报错
StackNode* p = new StackNode(); // 创建新节点
p->data = element; // 赋值
p->next = s->top; // 头插法
s->top = p; // 更新头结点
s->size++; // 节点数加一
return true;
}
弹出pop
删除部分参考链表的删除即可。
// 弹出元素pop
bool LinkPop(LinkStack* s)
{
if (s->size == 0) // 判断是否为空
return false; // 空栈不能弹出元素
StackNode* p = s->top; // 栈顶结点
s->top = s->top->next; // 更新栈顶指针
delete(p); // 删除结点
s->size--;
return true;
}