栈
栈本质上是一种受限制的线性表,先进后出。
从存储结构可以分成以下两种:顺序栈和链栈。简单点就是一个是数组形成的栈,另一个是链表形成的栈
1.顺序栈
1.创建栈的结构体
typedef int SElemType;
typedef struct
{
SElemType *base; // 栈底指针
SElemType *top; // 栈顶指针
int stacksize; // 栈可用的最大容量
} SqStack;
2.初始化操作
SqStack* InitStack(SqStack *S)//注意,这里很多书上写&取地址,但是c语言中处理不了,需要传入指针
{
// 构造一个空表
S->base = (SElemType*)malloc(MAXSIZE * sizeof(SElemType));
if (!S->base) {
exit(EXIT_FAILURE); // 存储分配失败
}
S->top = S->base; // top初始为base,空栈
S->stacksize = MAXSIZE; // stacksize置为栈的最大容量MAXSIZE
return S;//返回的是整个栈的指针
}
int main() {
SqStack s;//先构建一个代表栈的结构体
SqStack* p = InitStack(&s);//将结构体地址传入
printf("%d\t%d\n%d",p->base,p->top,p->stacksize);//还没数据,所以两个地址相同,为空表
// 使用 p 操作栈...
// 释放动态分配的内存
free(p->base);
return 0;
}
3.入栈
SqStack* push(SqStack* S,SElemType e)
{
if(S->top-S->base==MAXSIZE)
{
exit(-1);
}
*S->top++=e;//^^^^^^^^^
return S;
}
先解引用后进行指针上移,单目运算符的运算顺序从右向左。
4.出栈
SqStack* pop(SqStack* S,SElemType* p)
{
if(S->top==S->base)
{
exit(-1);
}
*p=*--S->top;//先下移动,然后进行解引用
return S;
}
入栈和出栈的时候会涉及到判断,如果想返回栈的同时也有判断的返回
因为返回值只能是一种类型 所以使用exit()
exit(0) 正常
exit(n)异常 (n不是0)
参考连接:C语言中的exit()函数_c语言exit-CSDN博客
5.取栈顶元素
SElemType gettop(SqStack* S)
{
if(S->top!=S->base)
{
return *(S->top-1);
}
}
6.完整代码
#include <stdio.h>
#include<stdlib.h>
typedef int SElemType;
#define MAXSIZE 100
typedef struct
{
SElemType *base; // 栈底指针
SElemType *top; // 栈顶指针
int stacksize; // 栈可用的最大容量
} SqStack;
SqStack* InitStack(SqStack *S)//注意,这里很多书上写&取地址,但是c语言中处理不了,需要传入指针
{
// 构造一个空表
S->base = (SElemType*)malloc(MAXSIZE * sizeof(SElemType));
if (!S->base) {
exit(EXIT_FAILURE); // 存储分配失败
}
S->top = S->base; // top初始为base,空栈
S->stacksize = MAXSIZE; // stacksize置为栈的最大容量MAXSIZE
return S;//返回的是整个栈的指针
}
SqStack* push(SqStack* S,SElemType e)
{
if(S->top-S->base==MAXSIZE)
{
exit(-1);
}
*S->top++=e;
return S;
}
SqStack* pop(SqStack* S,SElemType* p)
{
if(S->top==S->base)
{
exit(-1);
}
*p=*--S->top;
return S;
}
SElemType gettop(SqStack* S)
{
if(S->top!=S->base)
{
return *(S->top-1);
}
}
int main() {
SqStack s;//先构建一个代表栈的结构体
SqStack* p = InitStack(&s);//将结构体地址传入
printf("%d\t%d\n%d\n",p->base,p->top,p->stacksize);//还没数据,所以两个地址相同,为空表
// 使用 p 操作栈...
push(p,1);//入栈
int b=gettop(p);
printf("%d\t%d\n%d\t%d\n",p->base,p->top,p->stacksize,b);
SElemType a=9;
SElemType* pa=&a;
SqStack* p1=pop(p,pa);
printf("%d\t%d\n%d\t%d\n",p1->base,p1->top,p1->stacksize,a);//出栈
// 释放动态分配的内存
free(p->base);
return 0;
}
输出结果
28029536 28029536
100
28029536 28029540
100 1
28029536 28029536
100 1
2.链栈
1.创建结构体
typedef struct snode
{
int data;
struct snode* next;
}snode,*linkstack;
如果使用typedef重新命名之后,尽量统一使用一个名称,不然编译器经常出现指针指向不明确的警告
2.初始化(无头结点)
linkstack init()
{
linkstack s;//创建指针
s=NULL;//指针指向空
return s;
}
这里创建的为无头结点的链栈,头结点的存在不是很有必要。(头指针,头结点,首元节点需要分清)
一般将链表的头作为栈顶(top)
3.入栈
linkstack push(linkstack s,int e)
{
linkstack p=(snode*)malloc(sizeof(snode));
p->data=e;
p->next=s;
s=p;
return s;
}
创建空间——赋值——先调整会变化的指针(s->next指p,s变则p变化)——再调整不会变的(s)
和头插法创建链表很相似
4.出栈
linkstack pop(linkstack s,int* e)
{
if(s==NULL) exit(-1);
*e=s->data;
linkstack q=s;
s=s->next;
free(q);
return s;
}
类似于删除链表节点
创建指针存放节点——跨过该节点——删除节点空间
5.取栈顶元素
int gettop(linkstack s)
{
if(s!=NULL)
return s->data;
}
6.完整代码如下
#include <stdio.h>
#include<stdlib.h>
typedef struct snode
{
int data;
struct snode* next;
}snode,*linkstack;
linkstack init()
{
linkstack s;//创建指针
s=NULL;//指针指向空
return s;
}
linkstack push(linkstack s,int e)
{
linkstack p=(snode*)malloc(sizeof(snode));
p->data=e;
p->next=s;
s=p;
return s;
}
linkstack pop(linkstack s,int* e)
{
if(s==NULL) exit(-1);
*e=s->data;
linkstack q=s;
s=s->next;
free(q);
return s;
}
void showlist(snode* l)
{
snode* p=l;
while(p)
{
printf("%d\n",p->data);
p=p->next;
}
printf("数据已经输出完毕\n");
}
int gettop(linkstack s)
{
if(s!=NULL)
return s->data;
}
int main()
{
linkstack s1=init();
linkstack s2=push(s1,3);
showlist(s2);
linkstack s3=push(s2,2);
showlist(s3);//类似上篇文章中的头插法来添加链表数据
int* p=(int*)malloc(sizeof(int));//需要一段空间来储存出栈数据
linkstack s4=pop(s3,p);
printf("%d\t",*p);
showlist(s4);//
printf("%d",gettop(s4));
return 0;
}
输出结果
3
数据已经输出完毕
2
3
数据已经输出完毕
2 3
数据已经输出完毕
3