简书内代码已上传GitHub:点击我 去GitHub查看代码
顺序栈相关的应用实例已更新 :行编辑程序 、表达式求值 、迷宫求解
栈是重要的 线性结构
一.什么是栈?
栈是限定仅在表尾进行插入或删除操作的线性表。我们来看图理解:
顺序栈对栈来说,表尾称为栈顶,表头称为栈底。图中一开始top == base , 这是个空栈,因为 栈顶指针是指向下一个要添加的栈顶元素的位置 的,就像第二小图,A元素入栈后,top指针指向新的栈顶的位置。如果要删除元素,必须从栈顶开始删除。也就是说,A, B,C依次入栈,出栈的顺序应该是 C, B, A。这也是栈的一个特性,被称作 后进先出 (last in first out)。所以,栈又称为:后进先出的线性表,简称 LIFO结构。
我们来看一张很有历史意义的图片加深印象:
詹天佑修的栈
没错,就是京张铁路。火车从 位置1 到 位置2 , 其实就是一个入栈的过程。位置2相当于一个栈,当一列火车从头部开始驶入位置2 , 那先进入位置3 的就是火车的尾部了。
二.顺序栈的结构定义
顺序栈的存储结构是利用一组地址连续的存储单元依次存放栈底到栈顶的元素。
顺序栈需要两个指针top,base指向栈顶和栈底位置
需要一个变量存储栈的大小
定义如下:
typedef struct{
//基址,指向栈底
Elemtype* base;
//栈顶指针
Elemtype* top;
//栈空间大小(单位:元素)
int Stacksize;
}SqStack;
三.顺序栈的九种基本操作
1.构造一个空栈:InitStack(SqStack&)
在定义一个栈后,栈实际上只分配了2个指针变量和一个整型变量的空间,需要主动给栈分配空间存储元素.
//初始化栈(空栈)
Status InitStack(SqStack &S){
//给栈元素分配初始空间
S.base = (Elemtype*)malloc(sizeof(Elemtype)*INIT_SIZE);
//若内存分配失败,返回ERROR
if(!S.base){
printf("error: 内存分配失败\n");
return OVERFLOW;
}
//栈顶指针指向栈底
S.top = S.base;
//栈空间大小为初始化给的大小
S.Stacksize = INIT_SIZE;
return OK;
}
2.销毁一个栈:DestroyStack(SqStack&)
摧毁栈实际上就是把给栈元素分配的空间回收.
//摧毁栈
Status DestroyStack(SqStack &S){
//若栈未初始化,返回ERROR
if(!S.base){
printf("error: 栈未初始化\n");
return ERROR;
}
//释放栈内元素空间
free(S.base);
//栈顶栈底指针置空
S.base = S.top = NULL;
//栈大小归0
S.Stacksize = 0;
return OK;
}
3.清空一个栈:ClearStack(SqStack&)
清空栈实际上就是把栈顶指针指向栈底,其中分配的空间还在,只不过相当于被 “标记” 了, 栈顶指针 ~ 栈底指针 指向的范围内才是有效数据。
举个例子,平时我们删除文件, 其实文件也只是被“标记”了,所以在回收站里我们可以恢复文件。即使在回收站里彻底删除了,我们还可以通过其他手段尽可能多的恢复文件。
//清空栈
Status ClearStack(SqStack &S){
//若栈未初始化,返回ERROR
if(!S.base){
printf("error: 栈未初始化\n");
return ERROR;
}
//使栈顶指向栈底
S.top = S.base;
//栈大小归0
S.Stacksize = 0;
return OK;
}
4.判断一个栈是否为空栈:StackEmpty(SqStack&)
top == base && base != NULL 即为空栈
//判断S是否为空栈
Status StackEmpty(SqStack &S){
//若栈未初始化,返回ERROR
if(!S.base){
printf("error: 栈未初始化\n");
return ERROR;
}
//若栈底指针 == 栈顶指针 , 为空栈
if(S.base == S.top){
return True;
}
return False;
}
5.返回栈的长度(元素个数):StackLength(SqStack&)
栈的长度 不等于 栈的大小,他们的关系应该是 栈的长度 < 栈的大小
栈的长度 = S->top - S->base ,这里注意:两个同类型指针相减,结果不是以字节为单位的,而是以指针指向元素类型大小为单位。所以这里的S->top - S->base就是栈的长度
//返回栈里元素个数(栈的长度)
int StackLength(SqStack &S){
//如果栈未初始化,返回0
if(!S.base) return 0;
//栈长度 = (栈顶指针 - 栈底指针) / 栈元素大小
return S.top - S.base ;
}
6.返回栈顶元素到e:GetTop(SqStack&, Elemtype&)
栈顶元素是*(top - 1)元素
//返回栈顶元素
Status GetTop(SqStack &S, Elemtype &e){
//若栈是空栈,返回ERROR
if(S.top == S.base){
printf("error: 返回栈顶元素失败\n");
return ERROR;
}
//通过e返回栈顶元素
e = *(S.top - 1);
return OK;
}
7.插入新元素:Push(SqStack&, Elemtype)
如果栈长度*Elemtype_Size >= 栈大小 , 需要realloc更大的空间。
更新栈顶指针(重点看代码)
插入新的元素,移动栈顶指针
//压入元素e为新的栈顶元素
Status Push(SqStack &S, Elemtype e){
if(S.top - S.base >= S.Stacksize){
S.base = (Elemtype*)realloc(S.base, sizeof(Elemtype)* (S.Stacksize + INCREMENT_SIZE));
if(!S.base){
printf("error: 内存分配失败\n");
return OVERFLOW;
}
//若空间不足,重新分配,需要重新定位栈顶指针
S.top = S.base + S.Stacksize;
S.Stacksize += INCREMENT_SIZE;
}
*S.top++ = e;
return OK;
}
8.删除栈顶元素:Pop(SqStack& , Elemtype&)
通过e返回删除的栈顶元素
top指针自减
//弹出栈顶元素, 用e返回其值
Status Pop(SqStack &S, Elemtype &e){
//若为空栈,返回ERROR
if(S.base == S.top) return ERROR;
//移动栈顶指针后通过e保存栈顶元素的值
e = *--S.top;
return OK;
}
9.遍历栈:StackTraverse(SqStack, Status(visit)(Elemtype))
定义一个工作指针用visit函数遍历栈
//遍历栈,调用visit函数
Status StackTraverse(SqStack S, Status(*visit)(Elemtype*)){
for(Elemtype* p = S.base; p < S.top ; ++p){
Status s = visit(p);
if(s != OK){
printf("ERROR: StackTraverse False");
return ERROR;
}
}
return OK;
}
四.顺序栈的基本应用:
1.进制转换(十进制转二进制,其余类似):
把需要转换的数字按 低位~高位 进行转换
把转换的结果入栈
出栈并输出,这时候数字顺序就是 高位~低位 的顺序
代码如下:
void BaseConversion(){
SqStack s;
int N;
InitStack(s);
scanf("%d", &N);
while(N){
Push(s, N % 2);
N /= 2;
}
int e;
while(!StackEmpty(s)){
Pop(s, e);
printf("%d ", e);
}
}
测试:
测试
2.括号匹配的检验:
这个问题的一种巧解可以看 这一篇 ,现在我们用栈的特性也可以很轻松的实现。
其实上一种方法也是通过 类似栈的操作来达成的,而直接使用栈,思路更加清晰易懂。
下面是以上 链接 内同一题的栈实现
bool isValid(string s) {
SqStack S;
InitStack(S);
int len = s.length();
for(int i = 0 ; i < len ; ++i){
switch(s[i]){
//当检测到左括号,改为对应右括号存入栈中
case '(':
Push(S, ')');
break;
case '{':
Push(S, '}');
break;
case '[':
Push(S, ']');
break;
default:
//当第一次检测到右括号,此时需要满足:
// 1.右括号不是第一个出现的
// 2.栈顶元素与之匹配,
// 这里我们修改过左括号,所以只需要判断它们是否相等
char e;
GetTop(S, e);
if(s[i] != e || i == 0)
return false;
Pop(S, e);
}
}
return StackEmpty(S);
}
AC
五.完整代码:
#include
#include
//初始化时存储空间分配量
#define INIT_SIZE 100
#define INCREMENT_SIZE 10
//定义状态
#define OVERFLOW -1
#define OK 1
#define ERROR 0
#define True 1
#define False 0
//定义元素类型
typedef int Elemtype;
//定义状态类型
typedef int Status;
//顺序栈结构定义
typedef struct{
//基址,指向栈底
Elemtype* base;
//栈顶指针
Elemtype* top;
//栈空间大小(单位:元素)
int Stacksize;
}SqStack;
//初始化栈(空栈)
Status InitStack(SqStack &S){
//给栈元素分配初始空间
S.base = (Elemtype*)malloc(sizeof(Elemtype)*INIT_SIZE);
//若内存分配失败,返回ERROR
if(!S.base){
printf("error: 内存分配失败\n");
return OVERFLOW;
}
//栈顶指针指向栈底
S.top = S.base;
//栈空间大小为初始化给的大小
S.Stacksize = INIT_SIZE;
return OK;
}
//摧毁栈
Status DestroyStack(SqStack &S){
//若栈未初始化,返回ERROR
if(!S.base){
printf("error: 栈未初始化\n");
return ERROR;
}
//释放栈内元素空间
free(S.base);
//栈顶栈底指针置空
S.base = S.top = NULL;
//栈大小归0
S.Stacksize = 0;
return OK;
}
//清空栈
Status ClearStack(SqStack &S){
//若栈未初始化,返回ERROR
if(!S.base){
printf("error: 栈未初始化\n");
return ERROR;
}
//使栈顶指向栈底
S.top = S.base;
//栈大小归0
S.Stacksize = 0;
return OK;
}
//判断S是否为空栈
Status StackEmpty(SqStack &S){
//若栈未初始化,返回ERROR
if(!S.base){
printf("error: 栈未初始化\n");
return ERROR;
}
//若栈底指针 == 栈顶指针 , 为空栈
if(S.base == S.top){
return True;
}
return False;
}
//返回栈里元素个数(栈的长度)
int StackLength(SqStack &S){
//如果栈未初始化,返回0
if(!S.base) return 0;
//栈长度 = (栈顶指针 - 栈底指针) / 栈元素大小
return S.top - S.base ;
}
//返回栈顶元素
Status GetTop(SqStack &S, Elemtype &e){
//若栈是空栈,返回ERROR
if(S.top == S.base){
printf("error: 返回栈顶元素失败\n");
return ERROR;
}
//通过e返回栈顶元素
e = *(S.top - 1);
return OK;
}
//压入元素e为新的栈顶元素
Status Push(SqStack &S, Elemtype e){
if(S.top - S.base >= S.Stacksize){
S.base = (Elemtype*)realloc(S.base, sizeof(Elemtype)* (S.Stacksize + INCREMENT_SIZE));
if(!S.base){
printf("error: 内存分配失败\n");
return OVERFLOW;
}
//若空间不足,重新分配,需要重新定位栈顶指针
S.top = S.base + S.Stacksize;
S.Stacksize += INCREMENT_SIZE;
}
*S.top++ = e;
return OK;
}
//弹出栈顶元素, 用e返回其值
Status Pop(SqStack &S, Elemtype &e){
//若为空栈,返回ERROR
if(S.base == S.top) return ERROR;
//移动栈顶指针后通过e保存栈顶元素的值
e = *--S.top;
return OK;
}
//遍历栈,调用visit函数
Status StackTraverse(SqStack S, Status(*visit)(Elemtype* )){
for(Elemtype* p = S.base; p < S.top ; ++p){
Status s = visit(p);
if(s != OK){
printf("ERROR: StackTraverse False");
return ERROR;
}
}
return OK;
}
End
END