栈
本文为作者学习b站上交版<<数据结构与算法>>, 结合清华大学出版社<<数据结构(C语言版)>> 的学习笔记, 部分内容来自于视频中的的PPT
栈的定义
栈 : 一个只能在栈顶进行插入何删除的线性表, 特征为 LIFO.
空栈 : 不含元素的空表
遵循LIFO(Last In First Out)原则的线性表
堆栈的表示和实现
栈的表示
顺序栈操作
#define TRUE 1 //正确
#define FALSE 0 //错误
#define OK 1 //执行成功
#define ERROR 0 //执行有误
#define INFEASIBLE -1 //不可执行
#define OVERFLOW -2 //溢出
typedef int Status; //函数执行的结果, 返回值为 int 类型
typedef int ElemType; //用 int 型元素表示任意类型的元素
#define STACK_INIT_SIZE 100 //初始存储空间
#define STACKINCREMENT 10 //存储空间不够的时候, 每次扩容增加的存储空间
typedef int SElemType;
typedef struct {
SElemType* base; //栈底
SElemType* top; //栈顶
int stacksize; //当前可以使用的最大容量
}SqStack;
//代码来源 <<数据结构>> (清华大学出版社出版, 严蔚敏/吴伟民 编著)
base 为 NULL 则栈结构不存在
top 指向的是栈顶元素的下一个位置(下次入栈的位置)
top-1 指向出栈时下一个元素的取值位置
栈空 : top == base
栈满 : top >= base + stacksize
初始化
Status InitStack(SqStack& S){
S.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!S.base) exit(OVERFLOW); //内存不足
S.top = S.base; //空栈
S.stacksize = STACK_INIT_SIZE; //初始存储空间
return OK;
} //InitStack
//代码来源 <<数据结构>> (清华大学出版社出版, 严蔚敏/吴伟民 编著)
用malloc函数分配空间并进行判断, 将栈置空, 并且设置初始存储空间为 STACK_INIT_SIZE
销毁
Status StackDestory(SqStack& S)
{
free(S.base); //将内存释放
S.base = S.top = NULL; //指针置空
S.stacksize = 0; //容量归零
return OK; //返回状态码
} //StackDestory
销毁栈的代码如上.
判断是否为空
Status StackEmpty(SqStack S) {
return (S.base == S.top);
} //StackEmpty
如果栈S没有初始化, 则无法编译成功(VS2019 : 使用了未初始化的局部变量)
栈为空的条件为 栈顶指针与栈尾指针重合, 所以直接返回 判断栈顶指针与栈尾指针重合的结果
长度
int StackLength(SqStack S)
{
return (S.top - S.base);
}
这里直接返回 S.top - S.base 就是栈的长度
因为 : 两个指向同一个数组的指针相减, 得到的结果是两指针中间元素的个数
获得栈顶元素
Status GetTop(SqStack S, ElemType& e) {
if (S.base == S.top) return ERROR;
//栈未初始化, 返回错误
e = *(--S.top);
return OK;
} //GetTop
S.top指向的是栈顶元素的下一个元素, 所以在返回栈顶元素的时候应该将其先指向栈顶元素 即S.top–, 然后再将其指向的元素赋值给 e
因为栈 S 不能改变, 所以以值的方式传递, 元素e 以引用的方式传递.
入栈
Status Push(SqStack& S, SElemType e){
//元素e插入到栈中, 成为新的栈顶
if (S.top - S.base >= S.stacksize) {
//栈满
SElemType* newbase = (SElemType*)realloc(S.base,
(STACK_INIT_SIZE + STACKINCREMENT) * sizeof(SElemType));
if (!newbase)
exit(OVERFLOW); //空间不够, 退出
else
S.base = newbase;
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
*S.top++ = e;
return OK;
} //Push
//代码来源 <<数据结构>> (清华大学出版社出版, 严蔚敏/吴伟民 编著)
如果栈满了就将栈用realloc扩容. 每次扩容都增加10个元素的大小. 分配成功后, 将新的地址给S.base
然后将 e 的值赋给S.top(原先栈顶元素的下一个), 栈顶指针后移
出栈
Status Pop(SqStack& S, SElemType& e){
//从栈顶读取数据到e, 栈中下一个元素所在的位置成为新的栈顶
if (S.top == S.base)
return ERROR; //栈为空
e = *--S.top;
return OK;
} // Pop
//代码来源 <<数据结构>> (清华大学出版社出版, 严蔚敏/吴伟民 编著)
由于 S.top 指向的是末尾元素的下一个元素, 所以, 我们只需要把s.top–即可, 下一次使用这一块内存的时候会给他重新赋值, 不需要有其他的操作了
注意 :
入栈 value->top, top++
出栈 top–; top-value, 出栈后可以理解为"这个位置空了" 元素还在 只是这个元素不再使用了, 在下一次赋值的时候就被覆盖了
遍历输出
Status TraversePrint(SqStack S)
{
while (!StackEmpty(S)) {
printf("%d ", *(--S.top));
}
return OK;
}
注意, 这里要对S.top进行自减操作, 所以要用值传递.
每次都输出top指向的前一个位置的元素值, 直到 top 和 base 指针重合.
输出的顺序是从头到底
遵循先入后出原则
链栈操作
链栈
可以看出, 链栈每一个节点的指针域都指向其前方(下方)元素的next域, 而最后一个节点的指针域为NULL
初始化
typedef struct stack_node{
ElemType data;
struct stack_node* next;
}LinkStack;
Status InitLinkStack(LinkStack *stk){
stk.next = NULL;
return OK;
} //InitLinkStack
创造头节点.
将头结点的next置为空
判断是否为空
Status LinkStackEmpty(LinkStack* stk) {
return stk->next == NULL;
}//LinkStackEmpty
我们已经在初始化的时候将链栈置为空栈. 所以, 如果栈为空栈, 那么stk->next == NULL 的结果为1, 否则为 0.把结果直接返回即可.
入栈
Status PUSH(LinkStack& stk, ElemType x){
LinkStack top = (LinkStack)malloc(sizeof(LinkStack));
//创造节点
top->data = x; //数据赋值
top->next = stk; //next 指向下方元素
stk = top; //stk 指向栈顶元素
return OK;
}
创造一个栈节点, 其数据域赋值为 x, 并将新的节点的next域指向其下面的节点.
然后令stk继续指向新的栈顶.
弹出栈
int POP(LinkStack& stk){
//将栈顶元素赋值给 e 出栈
int e = stk->data; //栈顶元素赋值给 e
LinkStack p = stk; //中间变量 p 临时存放栈顶元素
stk = stk->next; //栈顶指针前移
free(p); //释放空间
return e;
} //POP
销毁
Status DestoryLinkStack(LinkStack& stk) {
while (!LinkStackEmpty(stk)){
POP(stk);
}
free(stk);
stk = NULL;
return OK;
}//DestoryLinkStack
将所有元素全部弹出, 释放掉stk所占的内存之后将stk的指向置空
长度
int Length(LinkStack& stk)
{
LinkStack temp = stk
int length = 0; //初始长度为 0
while (temp->next != NULL) {
temp = temp->next; //当stk->next非空的时候, 也就是没有到栈底
++length; //长度加一
}
return length; //返回栈长
}//Length
先用一个节点类型的变量存放stk, 用其进行遍历, 到栈底跳出循环, 返回长度.
获得栈顶元素
Status GETTOP(LinkStack& stk, ElemType& e){
// 获得栈顶元素
e = stk->data;
return OK;
}
由于stk指向的就是栈顶元素, 所以直接返回 stk的data即可
遍历输出
Status TraversePrintLink(LinkStack S)
{
LinkStack p = S;
while (!LinkStackEmpty(p)) {
printf("%d ", p->data);
p = p->next;
}
return OK;
}
当p未指向栈底元素的时候, 输出p->data并且向栈底方向移动.