栈是一个有具体操作限制的存储结构,栈的操作必须基于后进先出(Last In First Out, LIFO)
1.栈的定义
栈(stack)限定在栈尾进行插入和删除操作的线性表,我们将表尾段具有操作意义的指针称为栈顶(top),将表头段称为栈底(base).不含元素的空表则称为空表。
可以想象为堆起来的碟子无法从中间进行提取只能在碟子的顶部对这些碟子进行操作(也可以类比杯子装着刚好可以在表面取用的饼干)。
2.顺序栈的实现
顺序栈(通过顺序存储来实现栈的功能),利用栈底(base)到栈顶(top)的存储单元来存放数据元素
其中栈底指针是一直指向栈底且不进行任何操作,只需对栈的存储空间进行最开始的初始化,而栈顶指针则指向栈尾最后一个元素格的后一个单元格,即指向的是一个空格。
(1) 栈的存储结构
typedef struct {
int* top;//栈顶指针
int* base;//栈底指针
int maxsize;//栈的最大容量
}sqstack;
我这中间的具体实现是将栈用来存储整型元素,栈还可以根据不同的应用需求来定义栈顶和栈底指针。同时可以将maxsize放在宏定义上,使用#define maxsize n;即可。
(2)栈的初始化
void sqstack_init(sqstack* s, int maxsize)
{
s->base = (int*)malloc(sizeof(sqstack));//动态分配容量
if (s->base==NULL)//判断是否分配成功
{
return;
}
s->top = s->base;//空栈时栈顶top指向栈尾base
s->maxsize = maxsize;//将最大容量传入
}
在空栈时栈顶指针(s->top)与栈尾指针(s->base)是重合状态(均处于栈底位置),所以将top指针指向base ,还要考虑是否分配的空间存在才可以进行后继的操作。
(3)压栈(往栈中加入元素)
void sqstack_push(sqstack* s, int data)
{
if (s->top - s->base == s->maxsize)//判断是否满栈
{
printf("push failed\n");
return;
}
*s->top++=data;//将data放入top指向的单元格,然后将栈顶指向的前一个单元格
}
压栈要注意栈此时是否已经满栈,要是满栈就无法继续压元素入栈。将栈顶指针(s->top)向后移动一位再将元素压入栈顶(s->top)。
最后一步可以拆开来分析,是分两步走,先将元素放入top指针指向的单元格,再将top指针向后移动一位
s->top=data;//第一步先将元素放入单元格
++s->top;//第二步将栈顶指针向后移动一位
(4)出栈(从栈中提取元素)
int sqstack_pop(sqstack* s)
{
if (s->base == s->top)//判断是否是空表
{
return -1;
}
else
return -- * s->top;//返回s->top指向元素再将指针后移一位
}
先判断是否是空栈(用s->top是否等于s->base来判断,如果相等即栈为空),原来栈顶指针(s->top)是指向数据单元栈队的前一个空的单元格,要将top先向后移动一个单元格之后再返回s->top指针指向的元素。(同样可以分两步来理解,分步代码见下)
--s->top;//先向后移动一个单元格
return s->top;//再返回top指向的元素
(5)输出栈顶元素
int sqstack_gettop(sqstack* s)
{
if (s->base == s->top)//判断是否为空表
{
printf("get failed\n");
return -1;
}
else
return *s->top - 1;//返回s->top指向的元素
}
如果该表不为空栈即可提取出栈顶元素,所以要先判断是否为空栈再通过int函数返回指元素的值,但栈顶指针(s->top)所指向的是数据的·后一个单元格,要返回的值应该是(s->top)-1;
(6)完整代码以及实现
#include<stdio.h>
#include<stdlib.h>
typedef struct{
int* base;
int* top;
int stacksize;
}sqstack;
void sqstack_init(sqstack* s,int maxsize)//初始化
{
s->base=(int*)malloc(sizeof(int)*maxsize);
if(s->base==NULL)
{
return ;
}
s->top=s->base;
s->stacksize=maxsize;
return ;
}
void sqstack_push(sqstack* s,int data)//压栈
{
if(s->top-s->base==s->stacksize)
{
return ;
}
*s->top ++=data;
}
int sqstack_pop(sqstack* s)//出栈
{
if(s->top==s->base)
{
return -1;
}
return *--s->top;
}
int sqstack_gettop(sqstack* s)//取出栈顶元素
{
if(s->top==s->base)
{
return -1;
}
else
return *s->top-1;
}
int main()
{
sqstack s;
sqstack_init(&s,10);
int i;
for(i=0;i<10;i++)
{
sqstack_push(&s,i);
}
sqstack_pop(&s);//出栈操作取出了栈顶元素9,后面输出元素为8
printf("输出栈顶元素=%d",sqstack_gettop(&s));
printf("\n");
sqstack_pop(&s);//取出栈顶元素8之后就到元素7输出
printf("输出栈顶元素=%d",sqstack_gettop(&s));
return 0;
}
最后的实现验证创建的顺序栈是成功的,也可通过实现来加深对顺序栈的理解和栈的结构特点。
3.链表的实现
链栈是采用链式结构来实现的栈 ,和单链表的结构相同。
(1) 链栈的存储结构
typedef struct stacknode{
int data;
struct stacknode* next;
}stacknode, *Linkstack;
和单链表一样一个节点数据域(data)一个节点指针域(next),然后用Linkstack作为指向结构体stacknode的指针类型
(2)链栈的初始化
void stacknode_init(Linkstack*s)
{
*s=NULL;
}
初始化就是构造一个空栈,没有头节点。所以先构造一个空栈s,将栈顶指针置空,此时s是指向头节点的空指针。
(3)入栈
需要新建一个节点p来接收输入的数据,再将p接入到栈中去形成新的栈。
void stacknode_push(Linkstack* s,int data)
{
stacknode* p;//定义一个p节点来接受data元素
p=(stacknode*)malloc(sizeof(stacknode));//分配容量给构造的p节点
p->data=data;//p节点接受data的值
p->next=*s;//将节点p接在节点s后面
*s=p; //p赋给原来的头结点s
}
(4)出栈
任意定义一个同data一个类型的指针来接收头节点的元素(必须是与定义栈内数据元素一致的指针,否则无法接收到栈顶元素)。
void stacknode_pop(Linkstack* s,int* data)
{
if(s==NULL)//判断s是否是空表
{
return ;
}
*data=(*s)->data;
printf("出栈元素=%d",*data);//用data指针所指向元素输出对应data元素
stacknode* p =*s;//将栈顶元素存放在p里面,准别释放
*s=(*s)->next;//移动栈顶指针
free(p);//释放原来栈顶元素的空间
}
还要定义一个p节点来接收原来栈顶元素的空间之后进行删除来避免多出来的内存空间导致内存泄漏(在多次调用该函数之后数据出现错误)同时将栈顶元素进行移动就实现了出栈的功能。
(5)取栈顶元素
只要判断是否为空栈,在利用int函数直接返回栈顶空间的元素即可。
int stacknode_gettop(Linkstack* s)
{
if(s!=NULL)
{
return (*s)->data;//返回栈顶空间元素
}
else
{
printf("该栈不存在\n");
return -1;
}
}
(6)遍历输出链栈的各个节点的递归算法
因为next指针是指向链栈的本身,所以可以用递归来实现整个链栈元素的输出
void traverselist(Linkstack* s)
{
if(*s==NULL)
{
return ;
}
else
{
printf("%d",stacknode_gettop(s));
traverselist(&(*s)->next);//只要s栈顶节点非空就一直遍历下去
}
}
只要栈顶指针指向单元格存在元素就一直向下遍历来实现整表的输出。也可以简化如下
if(s)
{
printf("%d",stacknode_gettop(s));
traverselist(&(*s)->next);
}
(7)完整代码以及实现
#include<stdio.h>
#include<stdlib.h>
typedef struct stacknode{
int data;
struct stacknode* next;
}stacknode, *Linkstack;
void stacknode_init(Linkstack*s)//初始化
{
*s=NULL;
return ;
}
void stacknode_push(Linkstack* s,int data)//入栈
{
stacknode* p;
p=(stacknode*)malloc(sizeof(stacknode));
p->data=data;
p->next=*s;
*s=p;
}
void stacknode_pop(Linkstack* s,int* data)//出栈
{
if(s==NULL)
{
return ;
}
*data=(*s)->data;
printf("出栈元素=%d",*data);
stacknode* p =*s;
*s=(*s)->next;
free(p);
}
int stacknode_gettop(Linkstack* s)//取出栈顶元素
{
if(s!=NULL)
{
return (*s)->data;
}
else
{
printf("该栈不存在\n");
return -1;
}
}
void traverselist(Linkstack* s)//递归输出链栈元素
{
if(s)
{
printf("%d",stacknode_gettop(s));
traverselist(&(*s)->next);
}
}
int main()
{
Linkstack s;
stacknode_init(&s);
int i;
for(i=1;i<4;i++)
{
stacknode_push(&s,i);
}
int data;
stacknode_pop(&s,&data);//取出栈顶元素3
printf("\n");
printf("栈顶元素=%d",stacknode_gettop(&s));//取出3之后的栈顶元素即是2
printf("\n");
stacknode_push(&s,3);//再将3放回栈顶
printf("栈顶元素=%d",stacknode_gettop(&s)); //检验一下是否栈顶元素是3
printf("\n");
printf("输出栈列:\n");
traverselist(&s);//最后输出栈中所有元素
return 0;
}
如有错漏敬请指正