栈是限定只能在表的一端进行插入和删除的线性表,在表中,允许插入和删除的一端称为“栈顶”,不允许插入和删除的一端称为“栈底”。栈的元素进出是按照“先进后出,后进先出”的顺序进行。和线性表类似,栈也有两种存储方法顺序栈和链栈。
顺序栈顺序栈是指利用顺序存储分配实现的栈。同时附设top指针指示栈顶元素在顺序栈中的位置。用一维数组来描述顺序栈中数据元素的存储区域,并预设一个最大的存储空间。对于top指针,由于数组的第一个元素的下标为0,所以初始化top=-1;表示是个空栈。输出栈内数据元素时,可以利用循环变量 i <= S.top;来输出。由于顺序栈的插入和删除只能在栈顶进行,所以顺序栈的一些相关操作比顺序表要简单的多。具体C语言实现代码如下:
#include链栈#define TRUE 1 #define FALSE 0 const int STACK_INIT_SIZE = 100; //顺序栈默认的初始分配最大空间量 const int STACKINCREMENT = 10; //默认的增补空间量 typedef struct { int *elem; //存储数据的数组 int top; //栈顶指针 int stacksize; //当前分配的最大容量 int incrementsize; //约定的增补空间量 }SqStack; void InitStack_Sq(SqStack &S, int maxsize = STACK_INIT_SIZE, int incresize = STACKINCREMENT) { //构造一个空栈S,初始化分配的最大空间为maxsize,预设的增补空间量为incresize S.elem = new int[maxsize]; //为顺序栈分配最大容量为maxsize的数组空间 S.top = -1; //顺序栈中当前所含元素个数为零 S.stacksize = maxsize; //该顺序栈可容纳maxsize个数据元素 S.incrementsize = incresize; //需要时每次扩容incresize个元素的空间 } bool GetTop_Sq(SqStack S, int &e) { //若栈不空,用e返回栈顶元素并返回TRUE,否则返回FALSE if (S.top == -1) return FALSE; e = S.elem[S.top]; return TRUE; } void incrementStacksize(SqStack &S) { int *a; a = new int[S.stacksize + S.incrementsize]; for (int i = 0; i <=S.top; i++) a[i] = S.elem[i]; delete[]S.elem; S.elem = a; S.stacksize = S.stacksize + S.incrementsize; } void Push_Sq(SqStack &S, int e) { //插入元素e为新的栈顶元素 if (S.top == S.stacksize - 1) incrementStacksize(S); //若空间不够则扩容重新分配空间,类似于顺序表 S.elem[++S.top] = e; } bool Pop_Sq(SqStack &S, int &e) { //若栈不空,则删除S的栈顶元素,并用e返回其值,并返回TRUE,否则饭返回FALSE if (S.top == -1) return FALSE; e = S.elem[S.top--]; return TRUE; } void main() { int i; int e1; //用于接收栈顶元素 int e2 = 11; //用于插入新的栈顶元素 int e3; //用于接收删除的栈顶元素 SqStack S; InitStack_Sq(S); for (i = 0; i < 10; i++) { S.elem[i] = i + 1; S.top++; } printf("顺序栈中数据元素为:"); for (i = 0; i <= S.top; i++) printf("%d ", S.elem[i]); printf("n"); if (GetTop_Sq(S, e1)) printf("返回此时栈顶元素为:%dn", e1); else printf("这是一个空栈。n"); Push_Sq(S, e2); printf("插入新的栈顶元素后新栈的数据元素为:"); for (i = 0; i <= S.top; i++) printf("%d ", S.elem[i]); printf("n"); if (Pop_Sq(S, e3)) { printf("删除的栈顶元素为%d,删除后的栈内数据元素为:",e3); for (i = 0; i <= S.top; i++) printf("%d ", S.elem[i]); } else printf("这是一个空栈。"); }
链栈是利用链式分配实现的栈。对于顺序栈,虽然在满员状态下可以重新分配空间扩容,但是也是不得已而为之,所以当无法预先估计栈可能达到的最大容量时,应该采用链栈更合适。链栈的结点结构和链表的结点结构相同,值得注意的是,链栈中指针的方向是从栈顶指向栈底。
同顺序栈一样,链栈相比于单链表来说操作也简单了不少。值得注意的是,对于需要用 e 返回数值的函数来说,定义函数时形参部分需要用 int &e;来表示。原因是函数是布尔类型的函数,若要让e的值改变,需要传地址进入函数,所以用&e。同时,对于链栈的赋值,可以用提前赋好值的数组,也可以用scanf函数进行赋值,本质上是不断调用 void Push_L(linkStack &S, int e) 插入函数。
#include#define TRUE 1 #define FALSE 0 typedef struct LNode { int data; struct LNode *next; }LNode, *linkStack; void InitStack_L(linkStack &S) { //创建一个空的链栈,即设栈顶指针为空 S = NULL; } void Push_L(linkStack &S, int e) { //在链栈的栈顶插入新的栈顶元素e LNode *p = new LNode; //为新的栈顶元素分配结点 p->data = e; p->next = S; //插入新的栈顶元素 S = p; //修改栈顶指针 } bool Pop_L(linkStack &S, int &e) { //若栈不空则删除栈顶元素,并用e返回其值,并返回TRUE,否则返回FALSE if (S) { LNode *p; p = S; S = S->next; //修改栈顶指针 e = p->data; //返回栈顶元素 delete p; //释放结点空间 return TRUE; } else return FALSE; } void main() { int i; int e1; //用来接收新插入的数据元素 int e2; //用来返回删除的栈顶元素 int a[10] = { 1,2,3,4,5,6,7,8,9,10 }; linkStack S; LNode *p; InitStack_L(S); for (i = 0; i < 10; i++) { e1 = a[i]; Push_L(S, e1); } printf("此链栈的数据元素为:"); for (p = S; p != NULL; p = p->next) printf("%d ", p->data); printf("n"); if (Pop_L(S, e2)) { printf("删除的栈顶元素为%d,删除后的栈内数据元素为:", e2); for (p = S; p != NULL; p = p->next) printf("%d ", p->data); } else printf("这是一个空栈。"); }
本笔记所依据的教材为严薇敏版的《数据结构及应用算法教程》
所引用的图片来源于华中师范大学云课堂
所有代码在Visual Studio 2017上均可正常运行
如有错误欢迎指出



