一.什么是栈结构?
堆栈又名栈(stack),它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
(以上内容引用自百度百科)
二.顺序栈
1.什么是顺序栈?
- 顺序栈就是采用连续的内存存放数据的栈结构。
- 顺序栈的特点就是,先进后出,或者后进先出。
- 若栈已满不能入栈,若栈为空,不能出栈。
2.顺序栈的基本结构
- 顺序栈需要有基地址,用来指向给栈分配的内存的首地址。
- 顺序栈需要有一个栈顶指针,栈顶指针指向栈顶,但是因为是顺序栈,内存是连续的所以我们就用整数top来模拟这个栈顶指针的功能。
- 顺序栈还需要一个变量stacksize来表示当前栈的最大容量,如果top跟stacksize相等,那就说明当前栈满了,需要考虑增加栈空间。
3.顺序栈的基本实现
- 首先我们将准备工作做好。先要写好需要使用的头文件跟以后会用到的定义宏。
#include<stdio.h> #include<stdlib.h> #define Initial_stack_size 5//表示最开始的栈容量 #define ADD_stack_size 5//表示以后栈容量不够时,每次增加的容量 struct stack//栈的基本元素 { int* base;//基地址,也就是顺序栈的内存的首地址 int top;//栈顶,开始让它为0或-1都可以,只是使用时有略微差别(也可以表示当前栈的元素个数) int stacksize;//栈的当前容量 };
- 这样我们就把栈的基本元素组成写好了,接下来我们需要创建一个栈。这里我们创建栈之后,将栈的基地址赋给指针s。代码如下
struct stack st;//创建一个栈 struct stack* s = &st;//将栈的地址给全局变量s,方便函数调用
- 那我们现在已经创建一个栈,我们需要对栈进行初始化,比如开辟空间,赋值等等。要注意这里在申请栈的空间时,申请的是初始容量,如果后续容量不够,还需增加容量。
void inital_stack()//对栈进行初始化 { s->base = (int *)calloc(Initial_stack_size,sizeof(int));//为栈申请空间,这里使用calloc在申请空间的时候直接初始化为0 s->stacksize = Initial_stack_size;//初始容量 s->top = 0;//开始栈顶为0 }
- 现在,一个基本的栈已经创建好了,而且还为栈结构申请了空间,那么现在我们就要进行入栈的操作(存放数据)。要注意的是,栈这种结构,一定是,先将数据存放在栈结构的底层,然后逐步向上存放数据。
- 那如果在入栈时,我们刚才申请的空间已经存满了应该怎么办呢?我们需要修改刚才所申请的空间大小,我们使用realloc函数进行对申请空间的修改。
void ADD_stacksize()//增加栈的容量的函数 { int* newbase = (int*)realloc(s->base, sizeof(int) * (Initial_stack_size + ADD_stack_size));//修改栈的容量 s->stacksize += ADD_stack_size;//将当前栈的容量也改变 s->base = newbase;//更新基地址 } void ENTER_stack(int n)//将指定数据入栈(入栈函数)这里的n就是要存入栈的数据 { if (s->top >= s->stacksize)//如果top的值大于等于stacksize的值,说明当前栈的容量已经满了,需要增加栈的容量,那就调用增容函数 ADD_stacksize(); s->base[s->top] = n;//将数据存入栈 s->top++;//更新栈顶 //其实将数据入栈跟更新栈顶可以简化成这一行代码 //s->base[s->top++] = n; }
- 显示一个栈的所有元素。我们要注意的是在显示栈结构中存放的元素时,一定是从栈顶开始到栈底结束。
void SHOW_stack()//打印栈内当前的元素 { if (s->top == 0) { printf("栈为空!!!\n"); return; } printf("当前栈内的元素为:\n"); for (int i = s->top - 1; i >= 0; i--)//从栈顶元素开始向占栈底元素打印元素 printf("%d\n", s->base[i]); }
- 下面我们演示出栈的相关操作。出栈跟入栈刚好相反,出栈时应当从栈顶元素开始,如果是空栈的话,我们就不能出栈。而我们如果不是空栈,满足出栈的条件的话,就应该先求出出栈的元素是哪个。打印他在进行出栈
void GET_TOP_OF_stack()//求出栈顶元素 { if (s->top == 0) { printf("当前栈为空栈,栈顶无元素!!!"); return; } printf("栈顶元素为: %d\n", s->base[s->top-1]);//这里求出栈顶元素 } void GET_OUT_stack()//出栈函数 { if (s->top == 0) { printf("为空栈,无法出栈!!!"); return; } GET_TOP_OF_stack();//求出将要出栈的栈顶元素 s->top--;//出栈,并且更新栈顶 printf("!!!已出栈!!!\n"); }
- 求出栈结构当前所有的元素个数。我们可以知道,栈中元素个数其实就是top的值,所以在函数中我们直接返回top的值,并且打印
nt NUM_OF_stack()//求出当前栈内的元素个数 { return s->top;//直接返回top的值 }
- 接下来我们来实现栈结构的清除与摧毁。那清除仅仅是让栈顶回到原始位置,清除栈中保存的数据,并不会摧毁栈的结构,也就是说,清除栈之后,还可以重新使用这个栈。如果是摧毁这个栈的话,直接消除了这个栈的结构,也就是说,以后都不能再使用这个栈了。
void CLEAR_stack()//清除栈的所有元素但是不摧毁栈的结构 { s->top = 0; } void DESTROY_stack()//摧毁栈的结构 { free(s->base);//释放申请的空间 s->base = NULL; s->stacksize = s->top = 0; printf("栈已摧毁!!!\n"); }
- 现在附上完整代码以及测试的运行结果
#include<stdio.h> #include<stdlib.h> #define Initial_stack_size 5//表示最开始的栈容量 #define ADD_stack_size 5//表示以后栈容量不够时,每次增加的容量 struct stack//栈的基本元素 { int* base;//基地址,也就是顺序栈的内存的首地址 int top;//栈顶,开始让它为0或-1都可以,只是使用时有略微差别(也可以表示当前栈的元素个数) int stacksize;//栈的当前容量 }; struct stack st;//创建一个栈 struct stack* s = &st;//将栈的地址给全局变量s,方便函数调用 void inital_stack()//对栈进行初始化 { s->base = (int *)calloc(Initial_stack_size,sizeof(int));//为栈申请空间,这里使用calloc在申请空间的时候直接初始化为0 s->stacksize = Initial_stack_size;//初始容量 s->top = 0;//开始栈顶为0 } void ADD_stacksize()//增加栈的容量的函数 { int* newbase = (int*)realloc(s->base, sizeof(int) * (Initial_stack_size + ADD_stack_size));//修改栈的容量 s->stacksize += ADD_stack_size;//将当前栈的容量也改变 s->base = newbase;//更新基地址 } void ENTER_stack(int n)//将指定数据入栈(入栈函数)这里的n就是要存入栈的数据 { if (s->top >= s->stacksize)//如果top的值大于等于stacksize的值,说明当前栈的容量已经满了,需要增加栈的容量,那就调用增容函数 ADD_stacksize(); s->base[s->top] = n;//将数据存入栈 s->top++;//更新栈顶 //其实将数据入栈跟更新栈顶可以简化成这一行代码 //s->base[s->top++] = n; } void GET_TOP_OF_stack()//求出栈顶元素 { if (s->top == 0) { printf("当前栈为空栈,栈顶无元素!!!"); return; } printf("栈顶元素为: %d\n", s->base[s->top-1]);//这里求出栈顶元素 } void GET_OUT_stack()//出栈函数 { if (s->top == 0) { printf("为空栈,无法出栈!!!"); return; } GET_TOP_OF_stack();//求出将要出栈的栈顶元素 s->top--;//出栈,并且更新栈顶 printf("!!!已出栈!!!\n"); } void SHOW_stack()//打印栈内当前的元素 { if (s->top == 0) { printf("栈为空!!!\n"); return; } printf("当前栈内的元素为:\n"); for (int i = s->top - 1; i >= 0; i--)//从栈顶元素开始向占栈底元素打印元素 printf("%d\n", s->base[i]); } int NUM_OF_stack()//求出当前栈内的元素个数 { return s->top;//直接返回top的值 } void CLEAR_stack()//清除栈的所有元素但是不摧毁栈的结构 { s->top = 0; } void DESTROY_stack()//摧毁栈的结构 { free(s->base);//释放申请的空间 s->base = NULL; s->stacksize = s->top = 0; printf("栈已摧毁!!!\n"); } int main() { inital_stack();//调用栈的初始化函数,为栈开辟空间和初始化等等操作 int n;//存放数据个数 printf("请输入你想要存入栈的数据个数:"); scanf("%d", &n); //以下是入栈的示例 for (int i = 1; i <= n; i++)//调用入栈函数将1到n存入栈 ENTER_stack(i); SHOW_stack();//调用显示栈的函数,打印当前栈内的所有元素 //以下是出栈的示例 GET_OUT_stack();//调用出栈函数,将栈顶元素出栈 SHOW_stack();//打印出栈后当前栈的元素 //以下是求出栈内元素个数的示例 int d = NUM_OF_stack();//用局部变量d来接受函数的返回值 printf("当前栈的元素个数为:%d\n", d); //以下是清除栈的示例 CLEAR_stack(); SHOW_stack(); //以下是摧毁栈的示例 DESTROY_stack(); return 0; }
三.链栈
1.链栈跟顺序栈的区别
- 顺序栈的内存分配是连续的。
- 链栈是基于链式储存结构的,在内存的分配上是随机的,一般情况下,都不会连续。
- 顺序栈的内存是通过动态内存分配的,需要考虑增加容量的问题。
- 链栈是基于链表的,只需要考虑节点的插入。
2.链栈的特点
-
链栈是链式结构,还需要满足先进后出的栈结构。
-
也就是说,我们入栈在创建节点时,一直在头部创建节点,出栈就删除尾部的节点。
-
链栈的结构就是单向链表加上栈结构的规则,实现比较简单。
-
下面我就附上简单的测试代码(不完整)
3.链栈的实现
#include<stdio.h>
#include<stdlib.h>
struct stacknode
{
int date;//数据域
struct stacknode* next;//指针域
};
struct stacknode* HEAD_OF_LINK_stack = NULL;//创建头指针并且初始化
void ENTER_LINK_stack(int n)//入栈函数(采用头插法创建新栈点)
{
struct stacknode* NEW_LINK_stack = (struct stacknode*)malloc(sizeof(struct stacknode));//为新栈点申请空间
NEW_LINK_stack->date = n;//存储数据
NEW_LINK_stack->next = HEAD_OF_LINK_stack;//链接
HEAD_OF_LINK_stack = NEW_LINK_stack;//更新头指针
}
void SHOW_LINK_stack()//打印当前栈内的所有元素
{
printf("链栈当前的元素为:\n");
struct stacknode* pc = HEAD_OF_LINK_stack;//创建临时变量,以便遍历打印
while (pc != NULL)
{
printf("%d\n", pc->date);//打印当前节点的数据
pc = pc->next;//移动到下一个节点
}
}
void GET_OFF_LINK_stack()//出栈函数
{
struct stacknode* pc = HEAD_OF_LINK_stack;
printf("当前栈顶元素为:%d\n", pc->date);//求出当前栈顶元素
pc = pc->next;
HEAD_OF_LINK_stack = pc;
printf("已出栈!!!\n");
}
void NUM_OF_LINK_stack()//求出链栈的元素个数
{
struct stacknode* pc = HEAD_OF_LINK_stack;
int cent = 0;
while (pc != NULL)
{
cent++;
pc = pc->next;
}
printf("当前栈内的元素个数为:%d", cent);
}
int main()
{
int n;
printf("请输入想要存入栈的数据的个数:");
scanf("%d", &n);
//下面是入栈的简单示例
for (int i = 1; i <= n; i++)
ENTER_LINK_stack(i);//调用入栈函数
SHOW_LINK_stack();//打印栈的数据
//下面是出栈的简单示例
GET_OFF_LINK_stack();
SHOW_LINK_stack();
//下面是求出链栈内当前元素个数的简单示例
NUM_OF_LINK_stack();
//摧毁和清除链栈的实现都非常简单,这里不再进行演示
return 0;
}
四.总结
本篇主要简单的实现了顺序栈,链栈的结构,但都是简单的实现,还没有具体的示例。链栈其余的内容比较简单,这里就不做演示,熟悉单链表就可以完成。如果还有其他错误,还请指正!