一、栈的定义
栈是只允许在一端进行插入或者删除的线性表。
在插入数据的时候只允许从栈顶插入,如图所示,在进行进栈的操作时,次序依次是a1,a2,a3,a4,a5;而出栈的时候次序则是a5,a4,a3,a2,a1,所以栈的操作特性是先进后出。
二、栈的顺序存储结构
1.顺序栈的实现
利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,并增加一个指针(top)指向当前栈顶的位置
#define MaxSize 50
typedef int ElemType;
typedef struct {
ElemType data[MaxSize]; //存放栈中元素
int top; //栈顶指针
}SqStack;
栈顶指针:S.top;初始化:S.top=-1;栈顶元素:S.data[S.top]
进栈操作:先检查栈是否满了,没满的话,则栈顶指针先+1,再送值到栈顶元素
出栈操作:先检查栈是否为空,不为空的话,则先取栈顶的值,再将栈顶指针-1
栈空的条件:S.top==-1;栈满的条件:S.top==MaxSize-1;栈长:S.top+1
2.顺序栈的基本运算
(1)栈的初始化
//初始化空栈
void InitStack(SqStack& S)
{
S.top = -1;
}
(2)判断栈是否为空
//判断是否为空栈
bool StackEmpty(SqStack& S)
{
if (S.top == -1)
return true;
else
return false;
}
(3)入栈操作
//入栈
bool Push(SqStack& S, ElemType x)
{
if (S.top == MaxSize - 1) //判断栈是否满了
{
return false;
}
S.data[++S.top] = x; //指针先加1,在入栈
return true;
}
(4)出栈操作
//出栈
bool Pop(SqStack& S, ElemType x)
{
if (S.top == -1)
{
return false;
}
x = S.data[S.top--]; //先出栈之后指针再减1
return true;
}
(5)读取栈顶元素
//读取栈顶元素
bool GetTop(SqStack S, ElemType& x)
{
if (S.top == -1)
{
return false;
}
x = S.data[S.top]; //x存放栈顶的元素
return true;
}
三、栈的链式存储结构
1.链栈的实现
链栈的优点是便于多个栈共享存储空间和提高效率,且不存在栈满上溢的情况。栈的链式存储类型如下:
typedef int ElemType;
typedef struct Linknode {
ElemType data; //数据域
struct Linknode* next; //指针域
}Linknode, * LiStack; //栈的类型定义
2.链栈的基本运算
(1)初始化
//初始化
void InitLiStack(LiStack& S)
{
S = NULL;
}
//判断栈是否为空
bool LiStackEmpty(LiStack S)
{
if (S == NULL)
return true;
else
return false;
}
(2)入栈操作
//入栈
bool push(LiStack& S, ElemType x)
{
Linknode* p;
p = (LiStack)malloc(sizeof(Linknode)); //新建结点,为其分配空间
p->data = x; //将新结点数据域赋值为X
p->next = S; //将指向原栈顶结点的指针用p->next替换
S = p; //将指向新结点的指针用S替换,以方便新结点的入栈
return true;
}
(3)出栈操作
//出栈
bool pop(LiStack& S, ElemType x)
{
Linknode* p;
if (S == NULL)
{
return false;
}
else {
x = S->data; //将栈顶元素数据域赋值给x
p = S; //将指向栈顶元素的指针替换为p
S = S->next; //S->next指向下一结点,现用S指向这个结点,相当于删除了原先的S->next
free(p); //释放结点p(即原栈顶元素)
p = NULL;
}
return true;
}
(4)获取栈顶元素
//获取栈顶元素
bool GetTop(LiStack S, ElemType& x)
{
if (S == NULL)
{
return false;
}
else {
x = S->data;
}
return true;
}
链式存储一系列操作与链表几乎相似,入栈和出栈都是在表头进行。
三、整体代码
顺序存储:
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 50
typedef int ElemType;
typedef struct {
ElemType data[MaxSize];
int top;
}SqStack;
//初始化空栈
void InitStack(SqStack& S)
{
S.top = -1;
}
//判断是否为空栈
bool StackEmpty(SqStack& S)
{
if (S.top == -1)
return true;
else
return false;
}
//入栈
bool Push(SqStack& S, ElemType x)
{
if (S.top == MaxSize - 1) //判断栈是否满了
{
return false;
}
S.data[++S.top] = x; //指针先加1,在入栈
return true;
}
//出栈
bool Pop(SqStack& S, ElemType x)
{
if (S.top == -1)
{
return false;
}
x = S.data[S.top--]; //先出栈之后指针再减1
return true;
}
//读取栈顶元素
bool GetTop(SqStack S, ElemType& x)
{
if (S.top == -1)
{
return false;
}
x = S.data[S.top]; //x存放栈顶的元素
return true;
}
int main()
{
SqStack S;
InitStack(S);
ElemType x;
bool flag;
Push(S, 2);
Push(S, 5);
Push(S, 9);
flag = StackEmpty(S);
if (flag)
{
printf("此栈为空栈:\n"); //因为上面插入了数据,所以运行不显示此语句
}
flag = GetTop(S, x);
if (flag)
{
printf("获取栈顶元素为:%d\n", x);
}
flag = Pop(S, x);
if (flag)
{
printf("弹出栈顶元素为:%d\n", x);
}
return 0;
}
链式存储:
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct Linknode {
ElemType data; //数据域
struct Linknode* next; //指针域
}Linknode, * LiStack; //栈的类型定义
//初始化
void InitLiStack(LiStack& S)
{
S = NULL;
}
//判断栈是否为空
bool LiStackEmpty(LiStack S)
{
if (S == NULL)
return true;
else
return false;
}
//入栈
bool push(LiStack& S, ElemType x)
{
Linknode* p;
p = (LiStack)malloc(sizeof(Linknode)); //新建结点,为其分配空间
p->data = x; //将新结点数据域赋值为X
p->next = S; //将指向原栈顶结点的指针用p->next替换
S = p; //将指向新结点的指针用S替换,以方便新结点的入栈
return true;
}
//出栈
bool pop(LiStack& S, ElemType x)
{
Linknode* p;
if (S == NULL)
{
return false;
}
else {
x = S->data; //将栈顶元素数据域赋值给x
p = S; //将指向栈顶元素的指针替换为p
S = S->next; //S->next指向下一结点,现用S指向这个结点,相当于删除了原先的S->next
free(p); //释放结点p(即原栈顶元素)
p = NULL;
}
return true;
}
//获取栈顶元素
bool GetTop(LiStack S, ElemType& x)
{
if (S == NULL)
{
return false;
}
else {
x = S->data;
}
return true;
}
int main()
{
LiStack S;
InitLiStack(S);
ElemType x;
bool ret;
push(S, 2);
push(S, 3);
push(S, 4);
push(S, 5);
ret = LiStackEmpty(S);
if (ret)
{
printf("此栈为空栈\n");
}
else
{
printf("此栈不为空\n");
}
ret = GetTop(S, x);
if (ret)
{
printf("获取栈顶元素为:%d\n", x);
}
ret = pop(S, x);
if (ret)
{
printf("弹出栈顶元素为:%d\n", x);
}
return 0;
}
总结
栈的话其实算是一个小知识点,需要记住对栈的操作是只能对某一端进行操作即可,顺序存储和链式存储大同小异,跟前面的线性表几乎一致。