栈:链栈和顺序栈的实现(C语言)

目录

一    了解栈

二    栈的实现

1, 栈的链式存储结构

2, 栈的顺序存储结构

 三    测试效果


一    了解栈

  • 概念: (Stack)是一种遵循先入后出(LIFO)的逻辑的线性数据结构。你可以把它的结构看做一个弹夹,子弹从弹夹上方压入弹夹(入栈/压栈),也只能从弹夹上方取出来(出栈)。由于栈的特点,所以限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。因此栈可以视为一种受限制的数组或链表
  • 应用: 每次调用函数时,系统都会在栈顶添加一个栈帧,用于记录函数的上下文信息。在递归函数中,向下递推阶段会不断执行入栈操作,而向上回溯阶段则会不断执行出栈操作。等


二    栈的实现

(1)定义链栈/顺序栈;                   (2)栈的初始化/构造;

(3)获取栈的长度;                        (4)判断栈是否为空;

(5)获取栈顶元素;                        (6)入栈;                

(7)出栈;                                      (8)销毁栈

1, 栈的链式存储结构

若不是很了解链式存储结构,可参考这篇文章:链表入门:“单链表“的基本操作详解(C语言)icon-default.png?t=N7T8https://blog.csdn.net/Mzyh_c/article/details/133841591?spm=1001.2014.3001.5502

 (1)定义链栈

typedef struct Stack
{
    //存储节点数据
	int data;
	struct Stack* next;
}LinkStack;
在main函数中成创建一个 top 指针变量,top指针变量在本代码中直接代表 栈顶结点。
	//初始化链栈
	LinkStack* top = InitStack();

(2)栈的初始化/构造

LinkStack* InitStack()
{
	LinkStack* top = (LinkStack*)malloc(sizeof(LinkStack));
	//指针top直接指向栈顶结点
	top = NULL;
    //返回指针top
	return top;
}

(3)获取栈的长度

int GetLength(LinkStack** top)
{
	//设立一个临时指针用于计算
	LinkStack* temp = *top;
	int count = 0;
	while (temp)
	{
		//从栈顶开始循环
		//当temp不为空指针时则count自增
		(temp) = temp->next;
		count++;
	}
	return count;
}

(4)判断栈是否为空

int IsEmpty(LinkStack** top)
{
    //当栈顶节点为空时则为空栈
	if (*top == NULL)
		return 0;//栈为空
	else
		return 1;//栈不为空
}

(5)获取栈顶元素

ElemType GetTop(LinkStack** top)
{
    //由于*top指针指向栈顶节点,所以可以直接访问栈顶元素
	return (*top)->data;
}

(6)入栈

void Push(LinkStack** top, int e)
{
	LinkStack* p = (LinkStack*)malloc(sizeof(LinkStack));
	if (!p)
		return;//创建失败则返回

	p->data = e;
	p->next = *top;
	*top = p;
}

注意:虽然链栈是基于链表的,但是当你使用本代码的写法时请不要用 top->next 去链接一个新的结点。这是一种错误的写法,如果这样做的话就会在出栈时无法找到前面的结点。

解析:出栈(push)函数中接收了结构体变量top的地址和一个int类型的变量e,首先创建了新结点 p 来存储要添加的数据,然后将变量 e 赋值给p的数据域,将栈的头结点(栈顶)赋值给 p 的next指针,此时 p->next 这个指针指向了原先的栈顶,最后再用新结点取代栈顶的头结点。(见下图)

链栈入栈的过程(动图)

(7)出栈

void Pop(LinkStack** top, ElemType* e)
{
	if (top == NULL)
		return;//空栈则返回
	//栈不为空时
	*e = (*top)->data;//带回数据
	//创建临时指针指向栈顶
	LinkStack* temp = *top;
	//栈顶变为后继的结点
	*top = (*top)->next;
	//释放临时指针指向的空间
	free(temp);
	//给临时指针置空,防止产生野指针
	temp = NULL;
}

(8)销毁链栈

void Destroy(LinkStack** top)
{
	if (!IsEmpty(top))
		return;
	int len = GetLength(top);
	LinkStack* temp = NULL;
	while (len--)
	{
		temp = *top;
		(*top) = (*top)->next;
		free(temp);		//释放空间
		temp = NULL;	//防止产生野指针
	}
	return;
}

2, 栈的顺序存储结构

因为栈只会在一端进行插入和删除操作,用数组效率会比较高,数组在尾上插入数据的代价比较小。触发扩容的话会导致效率降低,但由于扩容是低频操作,因此平均效率更高。

(1)基于数组实现的栈

typedef struct SqStack
{
	int* data;	    //存放数据(根据指针的偏移量,可以当数组用)
	int top;		//栈顶
	int capacity;	//栈的容量(使用大容量,防止扩容)
}SqStack;

(2)顺序栈的基本操作实现

/*    初始化顺序栈    */
void InitSqStack(SqStack* s)
{
	//给顺序栈开辟空间,base指向开辟好的空间的地址
	s->data = (ElemType*)malloc(sizeof(ElemType) * MAXSIZE);
	if (s->data == NULL)
		return;//开辟空间失败
	s->capacity = MAXSIZE;
	s->top = -1;//顺序栈为空时
}

/*    判断栈是否为空    */
int IsEmpty(SqStack* s)
{
	if (s->top == -1)
		return 1;//顺序表为空
	else
		return 0;//顺序表不为空
}

/*    获取栈顶元素    */
int GetTop(SqStack* s)
{
	return s->data[s->top];
}

/*    入栈    */
void Push(SqStack* s, int e)
{
	if (s->top == MAXSIZE)
	{
		return;//顺序栈满了则返回
	}
	else if (IsEmpty(s))
	{
		s->top = 0;//空栈则栈顶指向0索引
	}
	else
	{
		(s->top)++;//非空非满
	}
    //对栈顶数据进行赋值
	s->data[s->top] = e;
}


/*    出栈    */
int Pop(SqStack* s)
{
	if (IsEmpty(s))
	{
		return;//空栈则返回
	}	
    //获取栈顶元素进行返回
	int e = s->data[s->top];
	(s->top)--;
	return e;
}

/*获取顺序栈的长度*/
int GetLength(SqStack* s)
{
	return s->top + 1;
}

/*    清空顺序栈    */
void CleanStack(SqStack* s)
{
    //top指向-1,表示空栈
	s->top = -1;
}

/*    销毁顺序栈    */
void DestoryStack(SqStack* s)
{
	free(s->data);
	s->data = NULL;
	//空间释放后,s->data还记录着一块没有对应空间的地址,
	//为了防止野指针,将s->data置为空指针
	s->capacity = 0;
	s->top = -1;
}
顺序栈进栈过程

 三    测试效果

链栈测试效果

顺序栈测试效果

队列:栈队和顺序的实现icon-default.png?t=N7T8https://blog.csdn.net/Mzyh_c/article/details/135187848?spm=1001.2014.3001.5501

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值