数据结构之栈(顺序栈和链栈)(c语言附完整代码)

本文详细介绍了栈的定义及特性,包括顺序栈和链栈两种存储方式。顺序栈通过数组实现,链栈采用链表结构。文章列举了顺序栈和链栈的基本运算,如初始化、销毁、判断栈空、进栈、出栈和取栈顶元素,并提供了相应的C语言实现代码。此外,还讨论了这些运算的时间复杂度。
摘要由CSDN通过智能技术生成


一、定义

① 栈是一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作。
② 进行数据插入的删除和操作的一端,称为栈顶 ,另一端则称为 栈底
③ 栈中的元素遵守后进先出的原则,即 LIFO原则(Last In First Out)栈也称为后进先出表。
④栈的插入操作通常称为进栈或者入栈,栈的删除操作通常称为出栈退栈

示意图:
在这里插入图片描述

栈的顺序存储结构称为顺序栈

声明顺序栈

typedef struct 
{	
	ElemType data[MaxSize];
	int top;				//栈指针
} SqStack;	

栈的链式存储结构称为链栈

声明链栈

typedef struct linknode
{	
	ElemType data;				//数据域
	struct linknode *next;		//指针域
} LinkStNode;		

二、基本运算

顺序栈

设计顺序栈基本运算算法四要素
1.栈空的条件:s->top==-1;
2.栈满的条件:s->top==MaxSize-1(data数组最大下标)
3.进栈操作:先将栈顶指针top加1,然后将元素e放在栈顶指针处
4.出栈操作:先将栈顶指针处的元素取出并赋给e,然后将栈顶指针减1

初始化栈

void InitStack(SqStack *&s)
{
	s=(SqStack *)malloc(sizeof(SqStack));
	s->top=-1;
} 

该运算创建一个空栈,将s指向它,并将栈顶指针设置为-1。
本算法的时间复杂度为O(1)

销毁栈

void DestroyStack(SqStack *&s)
{
	free(s);
}

该运算用free函数释放栈s占用的空间。
本算法的时间复杂度为O(1)

判断栈是否为空

bool StackEmpty(SqStack *s)
{
	return(s->top==-1);
}

该运算判断栈是否为空,当s->top=-1时表示栈为空。
本算法的时间复杂度为O(1)

进栈操作

bool Push(SqStack *&s,ElemType e)
{
	if (s->top==MaxSize-1)    //栈满的情况,即栈上溢出
		return false;
	s->top++;
	s->data[s->top]=e;
	return true;
}

该运算插入一个数据元素,首先判断栈是否为已满,如果栈已满则返回 false,在栈不满的条件下先将栈顶指针增1,然后在该位置插入元素e,最后返回 true。
动画演示:
在这里插入图片描述

本算法的时间复杂度为O(1)

出栈操作

bool Pop(SqStack *&s,ElemType &e)
{
	if (s->top==-1)		//栈为空的情况,即栈下溢出
		return false;
	e=s->data[s->top];
	s->top--;
	return true;
} 

该运算删除一个数据元素,首先判断栈是否为空,如果为空则返回false,在栈不为空的条件下先将栈顶元素赋给e,然后再将栈顶指针减1,最后返回 true。
动画演示:
在这里插入图片描述

本算法的时间复杂度为O(1)

取栈顶元素

bool GetTop(SqStack *s,ElemType &e)
{
	if (s->top==-1) 		//栈为空的情况,即栈下溢出
		return false;
	e=s->data[s->top];
	return true;
}

该运算在栈不为空的的条件下将栈顶元素赋给e并返回 true,如果栈为空则返回false。
本算法的时间复杂度为O(1)

链栈

设计链栈(带头结点)基本运算算法四要素
1.栈空的条件:s->next==NULL
2.栈满的条件:由于只有内存溢出时才出现栈满,通常不考虑这样的情况,所以在链栈中可以看成不存在栈满
3.进栈操作:首先新建一个结点存放元素e(由p指向它),将结点p插入到头结点之后
4.出栈操作:取出首结点的值并将其删除

初始化栈

void InitStack(LinkStNode *&s)
{
	s=(LinkStNode *)malloc(sizeof(LinkStNode));
	s->next=NULL;
}

该运算创建一个空栈,就是创建链栈的头结点 并将其next域置为NULL。
本算法的时间复杂度为O(1)

销毁栈

void DestroyStack(LinkStNode *&s)
{
	LinkStNode *p=s->next;
	while (p!=NULL)
	{	
		free(s);
		s=p;
		p=p->next;
	}
	free(s);	//s指向尾结点,释放其空间
}

该运算释放链栈s占用的全部结点空间,和单链表的销毁算法相同。
本算法的时间复杂度为O(n)

判断栈是否为空

bool StackEmpty(LinkStNode *s)
{
	return(s->next==NULL);
}

该运算判断栈是否为空,就是判断s->next=NULL是否成立。
本算法的时间复杂度为O(1)

进栈操作

void Push(LinkStNode *&s,ElemType e)
{	LinkStNode *p;
	p=(LinkStNode *)malloc(sizeof(LinkStNode));
	p->data=e;				//新建元素e对应的结点p
	p->next=s->next;		//插入p结点作为开始结点
	s->next=p;
}

首先新建一个结点,用来存放元素e(由指针p指向它),然后将其插入到头结点之后(此处类似于创建单链表时的头插法),最后返回true。
本算法的时间复杂度为O(1)

出栈操作

bool Pop(LinkStNode *&s,ElemType &e)
{
	LinkStNode *p;
	if (s->next==NULL)		//栈空的情况
		return false;
	p=s->next;				//p指向开始结点
	e=p->data;
	s->next=p->next;		//删除p结点
	free(p);				//释放p结点
	return true;
}

首先判断栈是否为空,如果为空则返回false,在栈不为空的条件下将首结点的值赋给e(此处e为引用型参数),然后将其删除,最后返回true。
本算法的时间复杂度为O(1)

取栈顶元素

bool GetTop(LinkStNode *s,ElemType &e)
{
	if (s->next==NULL)		//栈空的情况
		return false;
	e=s->next->data;
	return true;
}

首先判断栈是否为空,如果为空则返回false,在栈不为空的条件下将首结点的数据域赋给e(e为引用型参数),最后返回true。
本算法的时间复杂度为O(1)

三 、小结

在这里插入图片描述

四 、完整代码

顺序栈

#include <stdio.h>
#include <malloc.h>
#define MaxSize 100
typedef int ElemType;
typedef struct 
{	
	ElemType data[MaxSize];
	int top;				//栈指针
} SqStack;					//顺序栈类型
void InitStack(SqStack *&s)
{
	s=(SqStack *)malloc(sizeof(SqStack));
	s->top=-1;
} 
void DestroyStack(SqStack *&s)
{
	free(s);
}
bool StackEmpty(SqStack *s)
{
	return(s->top==-1);
}
bool Push(SqStack *&s,ElemType e)
{
	if (s->top==MaxSize-1)    //栈满的情况,即栈上溢出
		return false;
	s->top++;
	s->data[s->top]=e;
	return true;
}
bool Pop(SqStack *&s,ElemType &e)
{
	if (s->top==-1)		//栈为空的情况,即栈下溢出
		return false;
	e=s->data[s->top];
	s->top--;
	return true;
} 
bool GetTop(SqStack *s,ElemType &e)
{
	if (s->top==-1) 		//栈为空的情况,即栈下溢出
		return false;
	e=s->data[s->top];
	return true;
}
int main()
{
	SqStack *s;     //定义一个栈
	InitStack(s);   //初始化栈 
	ElemType e;
	Push(s,1);
	Push(s,2);
	Push(s,3);
	GetTop(s,e);    // 取栈顶元素
	printf("当前栈顶元素为:%d\n",e);
	Pop(s,e);
	printf("出栈元素为:%d\n",e);
	printf("当前栈是否为空:%d\n",StackEmpty(s));
	DestroyStack(s);
	return 0;	
}

链栈

#include <stdio.h>
#include <malloc.h>
typedef int ElemType;
typedef struct linknode
{	
	ElemType data;				//数据域
	struct linknode *next;		//指针域
} LinkStNode;					//链栈类型
void InitStack(LinkStNode *&s)
{
	s=(LinkStNode *)malloc(sizeof(LinkStNode));
	s->next=NULL;
}
void DestroyStack(LinkStNode *&s)
{
	LinkStNode *p=s->next;
	while (p!=NULL)
	{	
		free(s);
		s=p;
		p=p->next;
	}
	free(s);	//s指向尾结点,释放其空间
}
bool StackEmpty(LinkStNode *s)
{
	return(s->next==NULL);
}
void Push(LinkStNode *&s,ElemType e)
{	LinkStNode *p;
	p=(LinkStNode *)malloc(sizeof(LinkStNode));
	p->data=e;				//新建元素e对应的结点p
	p->next=s->next;		//插入p结点作为开始结点
	s->next=p;
}
bool Pop(LinkStNode *&s,ElemType &e)
{	LinkStNode *p;
	if (s->next==NULL)		//栈空的情况
		return false;
	p=s->next;				//p指向开始结点
	e=p->data;
	s->next=p->next;		//删除p结点
	free(p);				//释放p结点
	return true;
}
bool GetTop(LinkStNode *s,ElemType &e)
{	if (s->next==NULL)		//栈空的情况
		return false;
	e=s->next->data;
	return true;
}
int main()
{
	LinkStNode *s;    //定义一个栈
	InitStack(s);     //初始化栈 
	ElemType e;
	Push(s,1);         //进栈1 
	Push(s,2);         //进栈2 
	Push(s,3);         //进栈3 
	GetTop(s,e);       // 取栈顶元素
	printf("当前栈顶元素为:%d\n",e);
	Pop(s,e);
	printf("出栈元素为:%d\n",e);
	printf("当前栈是否为空:%d\n",StackEmpty(s));
	DestroyStack(s);
	return 0;	
}

参考资料
李春葆《数据结构教程》(第五版)

  • 15
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello_world&&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值