目录
一、概述
栈ADT:
栈(Stack)是一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则。
在栈的操作中,一般将插入元素称为入栈(push),删除元素称为出栈(pop)。
栈分为两种,一种是顺序栈,一种是链栈。两种都是线性表的结构。
第一种对应数组实现,第二种则对应链表实现。
二、顺序栈(数组实现)
1、栈的结构
顺序栈的结构体通常为三个域:
▲容积大小:对应数据域的数组大小,栈的使用不可以超出其容积范围,否则造成数组越界。
▲栈顶位置:通常我们将array[0]设置为栈底,依次向后存储,栈顶位置即为最后存入元素的下标。
▲数据域(数组):可以动态分配内存的指针数组。
typedef struct Stack {
int capacity; //栈的容积
int top; //栈顶位置
int *array; //数组,存放数据
} *STACK, stack;
2、创建栈
传入栈的大小,返回栈的结构体指针。
STACK CreatStack(int num) {
STACK s = (STACK)malloc(sizeof(stack));
s->array = malloc(num * sizeof(int)); //动态申请数组
s->capacity = num;
s->top = -1; //栈顶位置为-1,即空
return s;
}
3、 释放栈
话不多说,将malloc的全都free了,注意防止对NULL操作就行。
void DisposeStack(STACK s) {
//确保栈是存在的
if(s) {
free(s->array); //释放数组
free(s); //释放结构体
}
}
4、检测是否为空栈
这个函数还是挺重要的,因为栈不允许对空栈进行pop操作,在pop前需要此操作来检验栈是否为空。
我们在创建一个空栈时,将top置为-1,数组下标不含-1,即表示空栈。
一旦top++,即插入一个元素,那么top = 0,指向数组下标为0的位置,即不是空栈了。
int IsEmpty(STACK s) {
return s->top == -1;
}
5、清空栈
栈顶直接置为-1即可。
void MakeEmpty(STACK s) {
s->top = -1;
}
6、入栈
入栈:添加元素。
栈只允许对栈顶操作,那么入栈肯定是将元素加到栈顶的下一个,并成为新的栈顶。
注意判断栈是否已满。
void push(int x, STACK s) {
if (s->top < s->capacity) //栈未满
s->array[++s->top] = x;
}
7、查看栈顶元素
注意判断栈是否为空,不要对空栈执行top。
int top(STACK s) {
if (!IsEmpty(s))
return s->array[s->top];
Error("Empty Stack");
return ERROR; //返回提示错误的常量
}
8、出栈
出栈:删除栈顶元素。
注意判断栈是否为空,不要对空栈执行pop。
void pop(STACK s) {
if (!IsEmpty(s))
s->top--;
}
9、出栈并返回
这个操作是比较实用的,注意别对空栈操作就好。
int TopAndPop(STACK s){
if(!IsEmpty(s))
return s->array[s->top--];
Error("Empty Stack");
return ERROR; //返回提示错误的常量
}
三、总结一些细节
常见的bug点:
1、对空栈执行pop和top操作
2、对满栈执行push操作
学习总结:
对于栈的实现,不一定如上封装各个函数,可以灵活变通。一是你不一定需要所有函数,二是这要写如果反复利用率不高其实没必要。
我总结为一个个函数只是为了方便表示,展示出栈的每个操作实现的思想。你完全可以通过具体你需要的表示来使用,具体是写成函数还是直接码上代码,看你自己的选择,灵活变通最好。
end
欢迎关注个人公众号“鸡翅编程”,这里是认真且乖巧的码农一枚,旨在用心写好每一篇文章,平常会把笔记汇总成推送更新~