👍数据结构——栈(c)-受限线性表
文章目录
本文使用C语言,采用顺序表的存储方式完成栈的表示和实现,以及栈的应用(包括数制转换、括号匹配、迷宫求解、表达式求值等)。
提示:以下是本篇文章正文内容
一、栈的定义
栈Stack 是限定在表尾进行插入或删除操作的线性表。
特点 后进先出
相关定义:
①栈顶top:表尾端
栈顶指针分两种:一种指向栈顶元素,一种指向栈顶元素的下一位。
下文所述内容栈顶指针均指向栈顶元素。
②栈底bottom:表头端
③空栈:不含元素的空表
④入栈push:
⑤出栈pop:
二、栈的顺序存储结构
顺序栈的定义:
//----- 栈的顺序存储表示 ------//
#define STACK_INIT_SIZE 100 /*存储空间初始分配量*/
#define STACKINCREMENT 10 /*存储空间分配增量*/
typedef struct{
int *base; //栈底指针
int *top; //栈顶指针
int stacksize; //当前已分配的存储空间,以元素为单位
}SqStack;
基本操作:
//----- 基本操作的函数原型说明 ------//
void InitStack(SqStack *S); //初始化栈
void DestoryStack(SqStack *S); //销毁栈
void ClearStack(SqStack *S); //清空栈
bool StackEmpty(SqStack *S); //判断栈是否为空
int StackLength(SqStack *S); //返回栈的长度
int GetTop(SqStack *S); //获取栈顶元素
void Push(SqStack *S, int e); //入栈
void Pop(SqStack *S, int *e); //出栈
具体实现:
1.初始化栈S
void InitStack(SqStack *S){
S->base = (int *)malloc(STACK_INIT_SIZE * sizeof(int));
if(!S->base)
printf("内存分配失败!");
S->top = S->base;
S->stacksize = STACK_INIT_SIZE;
}
2.销毁栈S
void DestoryStack(SqStack *S){
free(S->base);
S->base = NULL;
S->top = NULL;
S->stacksize = 0;
}
3.将栈S清为空栈
void ClearStack(SqStack *S){
S->top = S->base;
}
4.判断栈S是否为空
bool StackEmpty(SqStack *S){
if(S->base==S->top) return true;
else return false;
}
5.获取栈S的长度
int StackLength(SqStack *S){
int n;
n = S->top - S->base;
return n;
}
6.获取栈顶元素,但不从栈中移走
int GetTop(SqStack *S){
int e;
if(S->top == S->base)
return -1;
e = *(S->top - 1);
return e;
}
7.将元素e放入栈顶
void Push(SqStack *S, int e){
if(S->top - S->base >= S->stacksize){
S->base = (int *)realloc(S->base, (S->stacksize+STACKINCREMENT)*sizeof(int));
if(!S->base) printf("内存分配失败!");
S->top = S->base + S->stacksize;
S->stacksize += STACKINCREMENT;
}
*S->top++ = e;
}
8.移走栈顶元素,同时由e带来该元素的值
void Pop(SqStack *S, int *e){
if(S->top == S->base)
printf("栈内没有元素!");
--S->top;
*e = *S->top;
}
三、共享栈
利用栈底位置相对不变的特性,可让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
共享栈是为了更有效地利用存储空间,两个栈的空间相互调节,只有在整个存储空间被占满时才发生上溢。
其存储数据的时间复杂度均为O(1),所以对存储效率没有影响。
四、栈的链式存储结构
采用链式存储的栈称为链栈。
优点:便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。便于节点的插入和删除。
注:通常采用单链表实现,并规定所有操作都是在单链表的表头进行的。
分为:带头结点的链栈和不带头结点的链栈(更推荐)
五、栈的应用
1.数制转换
问题描述:十进制数N和其他d进制数的转换是计算机实现计算的基本问题,其解决方法有很多,其中一个简单算法基于下列原理:
N = (N div d)× d + N mod d
现要编制一个满足下列要求的程序:对于输入的任意一个非负十进制整数,打印与其等值的八进制数。
代码如下:
//栈的基本操作如上所述不变
void conversion(){
SqStack S;
int n, e;
InitStack(&S);
printf("请输入一个非负十进制数:\n");
scanf("%d", &n);
while(n){
Push(&S,n%8);
n = n/8;
}
while(!StackEmpty(&S)){
Pop(&S, &e);
printf("%d", e);
}
}
2.括号匹配的检验
问题描述:一个算术表达式含圆括号、中括号、花括号,且它们可任意嵌套使用。
写一程序,判断任一算术表达式中所含括号是否正确配对。
检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。
左括号直接放进去,右括号与栈顶元素进行比较。
代码如下:
//需要对栈的基本操作做出相应的修改,具体不详述。
char GetTop(SqStack *S); //获取栈顶元素
void Push(SqStack *S, char e); //入栈
void Pop(SqStack *S, char *e); //出栈
bool BracketMatch(char c[]){
SqStack S;
InitStack(&S);
bool flag = 1;
char demo