栈-(简单讲解及基本操作)
🌈定义及基本操作
⭐️定义
栈(stack)是限定仅在表尾进行插入或删除操作的线性表。因此,对栈来说,表尾端有其特殊含义,称为栈顶(top),相应地,表头端称为栈底(bottom)。不含元素的空表称为空栈。假设栈S=(a1, a2, …, an),则称a1为栈底元素,an为栈顶元素。栈中元素按a1, a2,…, an的次序进栈,退栈的第一个元素应为栈顶元素。换句话说,栈的修改是按后进先出的原则进行的,如图(a)所示。因此,栈又称为后进先出(Last In First Out,LIFO)的线性表,它的这个特点可用图(b)所示的铁路调度站形象地表示
⭐️生活引用
洗干净的盘子总是逐个往上叠放在已经洗好的盘子上面,而用时从上往下逐个取用。栈的操作特点正是上述实际应用的抽象。在程序设计中,如果需要按照保存数据时相反的顺序来使用数据,则可以利用栈来实现。
⭐️基本操作
初始化栈
判断栈空
入栈
出栈
取栈顶元素
销毁栈
🌈栈的代码实现
⭐️顺序存储
📣头文件引用
#include<stdio.h>
#include<stdlib.h>
📣栈结构设计
#define MAXSIZE 100//定义长度
typedef int DataType;//此处根据所需的栈元素类别更改
typedef struct
{
DataType data[MAXSIZE];
int top;
}SeqStack,*PSeqStack; //PSeqStack p;定义p为栈指针
📣栈基本操作
📝初始化栈
//初始化栈
PSeqStack Init_SeqStack(void)
{
/*创建一个顺序栈,入口参数无,返回指向顺序栈的指针*/
PSeqStack S;
S = (PSeqStack)malloc(sizeof(SeqStack));
if (S)
S->top = -1;
return S;
}
📝判断栈空
//判断栈空
int Empty_SeqStack(PSeqStack S)
{
/*判断栈是否为空 入口参数:顺序栈 返回值1为空 0非空*/
if (S->top == -1)
return 1;
else
return 0;
}
📝入栈
//入栈
int Push_SeqStack(PSeqStack S, DataType x)
{
/*在栈顶插入一个新元素,入口参数:顺序栈 返回1表示成功 0表示失败*/
if (S->top == MAXSIZE - 1)
return 0;
else
{
S->top++;
S->data[S->top] = x;
return 1;
}
}
📝出栈
//出栈
int Pop_SeqStack(PSeqStack s, DataType* x)
{
/*删除栈顶元素 并保存在*x中 入口参数 顺序栈 返回值 1表示成功 0表示失败*/
if (Empty_SeqStack(s))
return 0; //栈空不能出栈
else {
*x = s->data[s->top];
s->top--;
return 1;
}
}
📝取栈顶元素
//取栈顶元素
int GetTop_SeqStack(PSeqStack s, DataType *x)
{
/*取出栈顶元素 入口参数:顺序栈 返回值 1表示成功 0表示失败*/
if (Empty_SeqStack(s))
return 0;
else
*x = s->data[s->top];
return 1;
}
📝销毁栈
//销毁栈
void Destroy_Seqstack(PSeqStack* s)
{
/*入口参数:要销毁的顺序栈指针地址 无返回值*/
if (*s)
free(*s);
*s = NULL;
return;
}
以上代码可全部复制到同一文件(Seqstack.h)中使用。
⭐️链式存储
📣头文件引用
#include<stdio.h>
#include<stdlib.h>
📣栈结构设计
📝定义栈结点
/*定义栈节点*/
typedef struct node
{
int data;
struct node *next;
}StackNode,*PStackNode;
📝定义栈
/*定义栈*/
typedef struct
{
PStackNode top;
}LinkStack,*PLinkStack;
📣栈基本操作
📝初始化栈
/*初始化空栈*/
PLinkStack Init_LinkStack(void)
{
/*入口参数无 返回链栈指针 失败则返回NULL*/
PLinkStack S;
S=(PLinkStack)malloc(sizeof(LinkStack));
if(S) S->top=NULL;
return S;
}
📝判断栈空
/*判断栈空*/
int Empty_LinkStack(PLinkStack S)
{
/*入口参数 链栈指针 返回1 表示栈空 返回0表示栈非空*/
return (S->top==NULL);
}
📝入栈
/*入栈*/
int Push_LinkStack(PLinkStack S,int x)
{
/*入口参数 链栈指针 入栈数据
返回1表示成功否则返回0*/
PStackNode P;
P=(PStackNode)malloc(sizeof(StackNode));
if(!P)
{
printf("内存溢出");
return 0;
}
P->data=x;
P->next=S->top;
S->top=P;
return 1;
}
📝出栈
/*出栈*/
int Pop_LinkStack(PLinkStack S,int *val)
{
/*入口参数 链栈指针 保存值*val 返回1表示出成功否则返回0*/
PStackNode p;
if(Empty_LinkStack(S))
{
printf("栈空");
return 0;
}
*val=S->top->data;
p=S->top;
S->top=S->top->next;
free(p);
return 1;
}
📝取栈顶元素
/*取栈顶元素*/
int GetTop_LinkStack(PLinkStack S,int *val)
{
/*入口参数 链栈指针 保存值*val 返回参数 1为成功0失败*/
if(Empty_LinkStack(S))
{
printf("栈空");
return 0;
}
*val=S->top->data;
return 1;
}
📝销毁栈
/*销毁栈*/
void Destroy_LinkStack(PLinkStack *Ls)//此处需用二级指针
{
/*入口参数 链栈指针地址 无返回值*/
PStackNode p,q;
if(*Ls)
{
p=(*Ls)->top;
while(p)
{
q=p;
p=p->next;
free(q);
}
free(*Ls);
}
*Ls=NULL;
return;
}
以上代码可全部复制到同一文件(PLinkStack.h)中使用。
🌈栈的案列
⭐️进制转换
十进制数N和其他d进制数的转换:
对于输入的任意一个非负十进制整数,输出与其等值的八进制数。上述计算过程是从低位到高位顺序产生八进制数的各个数位;而输出过程应从高位到低位进行,恰好和计算过程相反,因而我们可以使用栈来解决这个问题。在计算过程中依次将得到的余数压入栈中,计算完毕后,再依次弹出栈中的余数就是数制转换的结果。
⭐️括号匹配的检验
假设表达式中允许包含两种括号:圆括号和方括号,其嵌套的顺序随意,即([]())或[([][])]等为正确的格式,[(]或([())或(()])均为不正确的格式。检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。
当计算机接受了第一个括号后,它期待着与其匹配的第八个括号的出现,然而等来的却是第二个括号,显然第二个括号的期待急迫性高于第一个括号,此时第一个括号“[”只能暂时靠边,而迫切等待与第二个括号相匹配的、第七个括号“)”的出现。类似地,因等来的是第三个括号“[”,其期待匹配的程度较第二个括号更急迫,则第二个括号也只能靠边,让位于第三个括号。在接受了第四个括号之后,第三个括号的期待得到满足,消解之后,第二个括号的期待匹配就成为当前最急迫的任务了,……,依次类推。可见,这个处理过程恰与栈的特点相吻合。每读入一个括号,若是右括号,则或者使置于栈顶的最急迫的期待得以消解,或者是不合法的情况;若是左括号,则作为一个新的更急迫的期待压入栈中,自然使原有的在栈中的所有未消解的期待的急迫性都降了一级.
⭐️表达式求值
表达式求值是程序设计语言编译中的一个最基本问题,其实现是栈应用的又一个典型例子。“算符优先法”,是一种简单直观、广为使用的表达式求值算法。要把一个表达式翻译成正确求值的一个机器指令序列,或者直接对表达式求值,首先要能够正确解释表达式。算符优先法就是根据算术四则运算规则确定的运算优先关系,实现对表达式的编译或解释执行的。在表达式计算中先出现的运算符不一定先运算,具体运算顺序是需要通过运算符优先关系的比较,确定合适的运算时机,而运算时机的确定是可以借助栈来完成的。将扫描到的不能进行运算的运算数和运算符先分别压入运算数栈和运算符栈中,在条件满足时再分别从栈中弹出进行运算.
以上案列代码请查阅专栏
以上代码如有疑惑请联系作者,给个点赞再走哦!