在开始前,请牢记这句话:栈是一种先进后出的数据结构。
栈(stack)是限定仅在表的一端进行操作的数据结构,请联系我们前文所学的,设想一个单链表我们只能够对其链表的表尾结点进行操作,而操作也只能够进行插入一个新的结点与删除最末尾的这个结点两个操作,而这样强限制性的‘链表’,就是我们所说的栈。
让我们重新理顺一下定义:栈是一个线性的数据结构,规定这个数据结构只允许在其中一端进行操作,并禁止直接访问除这一端以外的数据。
如图:栈就像一个放球的单管桶,只允许球从桶的开口这一端取出,并且球先放入桶中则后从桶中拿出。
栈分为数组栈和链表栈,其区别是数组栈使用数组进行功能的模拟,实现较为快速和便利,而链表栈使用链表的思路去设计,实现较为麻烦,但是其稳定不易出错;在链表栈中又分为静态链表栈和动态链表栈,静态链表栈给定栈的空间大小,不允许超过存储超过给定数据大小的元素,而动态栈使用的是自动创建空间的方法进行创建,只要符合机器的硬件要求以及编译器的控制,其理论上是极大的。
说了那么多,我们以链表栈的动态链表栈为例子,进行栈的设计,在后文直接以栈一名字称呼动态链表栈,这也是各类语言标准模板中栈的实现方式。
首先是栈的结点设计,我们可以设计出两个结构体,一个结构体Node表示结点,其中包含有一个data域和next指针。
其中data表示数据,其可以是简单的类型(如int,double等等),也可以是复杂的结构体(struct类型);
next指针表示,下一个的指针,其指向下一个结点,通过next指针将各个结点链接。
目前的设计如同单链表,接下来,为这个进行限制性的设计,我们额外添加一个结构体,其包括了一个永远指向栈头的指针top和一个计数器count记录元素个数,(也可以设计成一个指针top和一个指针bottom分别指向栈头和栈尾)其主要功效就是设定允许操作元素的指针以及确定栈何时为空(count的方法是当count为0时为空,top和bottom方法就是两者指向同一个空间时为栈为空)
这里我采用的是top和count组合的方法。其代码可以表示为:
//栈的结点设计 //单个结点设计,数据和下一个指针 typedef struct node { int data; struct node *next; } Node; //利用上面的结点创建栈,分为指向头结点的top指针和计数用的count typedef struct stack { Node *top; int count; } Link_Stack;
如图:
入栈(push)操作时,我们只需要找到top所指向的空间,创建一个新的结点,将新的结点的next指针指向我们的top指针指向的空间,再将top指针转移,指向新的结点,即是入栈操作
其代码可以表示为:
//入栈 push Link_Stack *Push_stack(Link_Stack *p, int elem) { if (p == NULL) return NULL; Node *temp; temp=(Node*)malloc(sizeof(Node)); //temp = new Node; temp->data = elem; temp->next = p->top; p->top = temp; p->count++; return p; }
本文固定URL:https://www.dotcpp.com/course/102
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程