数据结构--栈
什么是栈?
- 首先先给大家讲一下栈是什么:
- 栈是一种特殊的线性表。特殊之处就在于对栈的操作的特殊。对于栈,只允许其在固定的一端进行插入和删除元素操作。不像普通的顺序表,链表,可以在任意位置进行删除插入元素的操作。
- 那么"进行数据插入和删除操作的一端"就被称作为栈顶,另一端被称为栈底。栈中的元素遵守着后进先出,或者说先进后出的原则。(因为只允许在一端进行插入删除,假设栈中已经存有元素,那么我要取出第一个存进去的元素,就要先把后面的元素一个个拿出来,才能把第一个存进去的元素取出来)
- 压栈:栈的插入操作叫做进栈、压栈、入栈(不同的叫法)。插入数据都在栈顶
- 出栈:栈的删除操作叫做出栈。删除数据,取出数据,也在栈顶。
栈的实现
- 栈的实现有两种实现方式,一种就是和顺序表类似,用数组实现,另一种则是通过单链表来实现栈。
实现代码
- 头文件.h
c#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <stdbool.h> typedef int SKDataType; typedef struct Stack { SKDataType *a; int top;//栈顶下标 int capacity;//容量 }SK; //初始化栈 void SKInit(SK* ps); //销毁栈 void SKDestroy(SK* ps); //插入数据,压栈 void SKPush(SK* ps,SKDataType x); //删除数据,出栈 void SKPop(SK* ps); //获取现在栈顶元素的值 SKDataType SKTop(SK* ps); //栈的大小 int SKSize(SK* ps); //判断栈是否为空 bool SKEmpty(SK* ps);
- 函数实现文件.c
c#include "stack.h" //初始化栈 void SKInit(SK* ps) { assert(ps); ps->a=NULL; ps->capacity=0; ps->top=0; } //销毁栈 void SKDestroy(SK* ps) { assert(ps); free(ps->a); ps->a=NULL; ps->top=ps->capacity=0; } //插入数据,压栈 void SKPush(SK* ps,SKDataType x) { assert(ps); if(ps->top==ps->capacity) { int newCapacity=ps->capacity==0 ? 4 :ps->capacity*2; SKDataType * tmp=(SKDataType*) realloc(ps->a,sizeof (SKDataType)*newCapacity); if(tmp==NULL) { perror("realloc failed"); exit(-1); } ps->a=tmp; ps->capacity=newCapacity; } ps->a[ps->top]=x; ps->top++; } //删除数据,出栈 void SKPop(SK* ps) { assert(ps); assert(ps->top>0); ps->top--; } //获取现在栈顶元素的值 SKDataType SKTop(SK* ps) { assert(ps); assert(ps->top>0); return ps->a[ps->top-1]; } //栈的大小 int SKSize(SK* ps) { assert(ps); return ps->top; } //判断栈是否为空 bool SKEmpty(SK* ps) { assert(ps); return ps->top==0; }
- 测试文件.c(仅供参考)
c#include "stack.h" void TestStack1() { SK st; SKInit(&st); SKPush(&st, 1); SKPush(&st, 2); SKPush(&st, 3); SKPush(&st, 4); SKPush(&st, 5); while (!SKEmpty(&st)) { printf("%d ", SKTop(&st)); SKPop(&st); } printf("\n"); SKDestroy(&st); } int main() { TestStack1(); return 0; }
函数讲解
- 栈的结构
c//取别名,方便更换数据类型,比如你要存char类型数据的时候,直接把int换成char,就可以全局替换数据了 typedef int SKDataType; typedef struct Stack { SKDataType *a; int top;//标记栈顶的工具 int capacity;//容量 }SK;
- 初始化栈
c//初始化栈 void SKInit(SK* ps) { assert(ps); ps->a=NULL; ps->capacity=0; ps->top=0; }
- 销毁栈
c//销毁栈 void SKDestroy(SK* ps) { assert(ps); free(ps->a); ps->a=NULL; ps->top=ps->capacity=0; }
- 销毁栈就没什么好说的了,就是把结构体(工具包)里面的本体(数组/栈)给free掉(将动态开辟的空间返还给系统),指针置空,其他值也置为零。
- 压栈
c//插入数据,压栈 void SKPush(SK* ps,SKDataType x) { assert(ps); if(ps->top==ps->capacity) { int newCapacity=ps->capacity==0 ? 4 :ps->capacity*2; SKDataType *tmp=(SKDataType*)realloc(ps->a,sizeof(SKDataType)*newCapacity); if(tmp==NULL) { perror("realloc failed"); exit(-1); } ps->a=tmp; ps->capacity=newCapacity; } ps->a[ps->top]=x; ps->top++; }
- 如果这部分的解析还是不清楚的话可以先往下看,看完就明白了。
- 出栈
c//删除数据,出栈 void SKPop(SK* ps) { assert(ps); assert(ps->top>0); ps->top--; }
- 出栈也很简单,我们不需要对栈内元素做什么,我们直接更改下标就好了。让top--,因为我初始化top=0嘛。所以top就是栈顶元素的下一个元素的下标,也就是栈顶元素下标+1,top--之后,原本的栈顶就变成了新栈顶的下一个元素。后续插入数据也不会影响,会被直接覆盖。
- 获取现在栈顶元素的值
c//获取现在栈顶元素的值 SKDataType SKTop(SK* ps) { assert(ps); assert(ps->top>0); return ps->a[ps->top-1]; }
- 这个也没什么好说的,top就是栈顶元素的下标+1,想要获得栈顶元素,top-1就可以了。
- 栈的大小(栈中含有的有效元素个数)
c//栈的大小 int SKSize(SK* ps) { assert(ps); return ps->top; }
- 因为我初始化top=0,每次插入元素,top就+1,所以top的大小也是有效元素的大小。
- 判断栈是否为空
c//判断栈是否为空 bool SKEmpty(SK* ps) { assert(ps); return ps->top==0; }
- 因为我初始化top=0,这个就标志了栈是没有元素的。和上面计算栈的大小差不多,每次插入元素top+1,所以就可以直接用top==0来判断了。返回的是bool类型,记得带上stdbool.h头文件。
- 测试函数说明
c#include "stack.h" void TestStack1() { SK st; SKInit(&st); SKPush(&st, 1); SKPush(&st, 2); SKPush(&st, 3); SKPush(&st, 4); SKPush(&st, 5); while (!SKEmpty(&st)) { printf("%d ", SKTop(&st)); SKPop(&st); } printf("\n"); SKDestroy(&st); } int main() { TestStack1(); return 0; }

- 那么栈的内容就大概讲完了。后面会给大家讲道题,帮助大家感受一下,栈的作用。