首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入解析数据结构之栈及其应用

深入解析数据结构之栈及其应用

作者头像
云泽808
发布2025-12-30 17:44:14
发布2025-12-30 17:44:14
450
举报

一、栈

1.1 概念与结构

栈:一种特殊的线性表(逻辑结构一定是线性的,物理结构不一定),其只允许再固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶 出栈:栈的删除操作叫做出栈,出数据也在栈顶

栈底层结构选型 栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。原因如下:

使用数组来实现栈时,栈的核心操作(入栈,出栈)只需操作数组尾部元素,时间复杂度都是O(1)

代码语言:javascript
复制
struct Stack
{
   int* arr;
   int size;
   int capacity;
}

使用链表来实现栈时,栈的核心操作(入栈,出栈)都可以在链表的头部进行操作,时间复杂度都是O(1)

代码语言:javascript
复制
struct Stack
{
   int data;
   struct Stack* next;
}

这样看好像二者没有差别,但数组的优势不止如此:

  1. 数组的元素在内存中连续存储,所以其访问元素通过索引直接定位,无需像链表那样遍历查找
  2. 数组无需存储额外的指针/引用信息,内存利用率更高。链表每个节点需要额外空间存储指针,尤其在元素数量较多时,额外开销显著。(就比如说这里要存储3个整型数据,用链表来实现栈的空间更大,因为用链表实现要有3个节点大小的空间,一个节点差不多8个字节,消耗的空间更大。数组实现的话,就是向操作系统申请几个整型空间,也就是4个字节4个字节的增加,消耗空间更小)
  3. 数组实现只需要维护一个顶部指针,逻辑简单。链表实现需要处理节点的创建、删除和指针维护,容易出错。

可以发现栈的物理结构取决于底层实现,如果用数组实现,底层就是连续的(线性的),用链表实现(不连续的)

1.2 栈的实现

1.2.1 初始化

函数声明:

在这里插入图片描述
在这里插入图片描述

这里虽然栈的结构和顺序表几乎一摸一样,但是不同的数据结构,其提供的方法是不一样的。栈只有一端开口,栈只能在栈顶入数据和出数据

函数定义:

测试文件:

在这里插入图片描述
在这里插入图片描述
1.2.2 销毁
代码语言:javascript
复制
//销毁
void STDesTroy(ST* ps);
在这里插入图片描述
在这里插入图片描述
1.2.3 入栈
代码语言:javascript
复制
//入栈---栈顶
void STPush(ST* ps, STDataType x);

栈顶在数组的尾部,接下来把size改为top更符合栈的结构,top指向栈顶的位置,刚好就是栈中有效数据个数

在这里插入图片描述
在这里插入图片描述

这里和顺序表类似,空间足够,插入一个数据,size++

空间不够的话,就需要先额外增容了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

补充:ps不能为空,否则对空指针进行解引用了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.2.4 判断栈是否为空

出栈之前有个操作是判断栈是否为空,栈为空无数据可出了

代码语言:javascript
复制
//判断栈是否为空
bool STEmpty(ST* ps);

栈顶为0的时候,说明栈中的有效数据个数为0

在这里插入图片描述
在这里插入图片描述
1.2.5 出栈
代码语言:javascript
复制
//出栈---栈顶
void STPop(ST* ps);

此时有5个空间,栈顶在下一个要插入数据的位置,栈顶对应的下标为4

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

出栈即少一个数据,栈顶要减减,此时栈中有4个有效的空间,只有3个有效的数据(3个有效的数据就是1,2,3,不包含4)

1.2.6 取栈顶元素
代码语言:javascript
复制
//取栈顶元素
STDataType STTop(ST* ps);

取栈顶并不是出栈,而是把栈顶这个元素返回

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这个操作叫循环出栈,输出结果体现出栈后进先出的规则

1.2.7 获取栈中有效元素个数
代码语言:javascript
复制
//获取栈中有效元素个数
int STSize(ST* ps);
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、完整源码

Stack.h

代码语言:javascript
复制
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

//定义栈的结构 - Stack是栈的名称
typedef int STDataType;
typedef struct Stack {
	STDataType* arr;//存储数据的数组
	int top;//top指向栈顶的位置,刚好就是栈中有效数据个数
	int capacity;//栈的空间大小
}ST;

//初始化
void STInit(ST* ps);
//销毁
void STDesTroy(ST* ps);

//入栈---栈顶
void STPush(ST* ps, STDataType x);
//出栈---栈顶
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);

//判断栈是否为空
bool STEmpty(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);

Stack.c

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"

//初始化
void STInit(ST* ps)
{
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

//销毁
void STDesTroy(ST* ps)
{
	//arr不能为空,为空的话都没有向操作系统中申请内存,就没有必要销毁了
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

//入栈---栈顶
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	//判断空间是否足够
	if (ps->top == ps->capacity)
	{
		//增容
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		//如果capacity为0,就给4,realloc第二个参数的单位为字节,这里要申请4个整型空间,而不是4个字节
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
	//空间足够
	ps->arr[ps->top++] = x;
}

//判断栈是否为空
bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

//出栈---栈顶
void STPop(ST* ps)
{
	assert(!STEmpty(ps));
	ps->top--;
}

//取栈顶元素
STDataType STTop(ST* ps)
{
	assert(!STEmpty(ps));
	return ps->arr[ps->top - 1];
}

//获取栈中有效元素个数
int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

test.c

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"

void test01()
{
	ST st;
	STInit(&st);
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	printf("size:%d\n", STSize(&st));
	//STPop(&st);
	while (!STEmpty(&st))
	{
		//取栈顶
		STDataType top = STTop(&st);
		printf("%d ", top);
		//出栈
		STPop(&st);
	}
	printf("\n");

	STDesTroy(&st);
}

int main()
{
	test01();
	return 0;
}

三、栈相关算法题

有效的括号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路:借助数据结构—栈,遍历字符串,左括号入栈,是右括号就取栈顶元素比较,是否匹配

在这里插入图片描述
在这里插入图片描述

第一种情况,匹配的话,就让左括号出栈,出栈之后,继续在字符串中遍历

在这里插入图片描述
在这里插入图片描述

这里用一个指针pi来遍历字符串,此时拿到一个右大括号,此时取栈顶取到的是一个左大括号,将其与* pi比较,是匹配的让左大括号出去

以此类推,循环往复,当pi遍历完字符串,此时栈为空,说明这就是一个有效的字符串

在这里插入图片描述
在这里插入图片描述

这里参数有一个char* s,s就是字符串,s指向字符串的第一个字符,循环截至的条件是遍历完字符串 基于上述思路代码如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里看似写完了,实则不然,对比案例测试一下,能否覆盖所有情况

在这里插入图片描述
在这里插入图片描述

这里只有左括号,入栈之后pi++,此时pi走到\0,跳出while循环,就直接销毁了,所以这里没有写全

在这里插入图片描述
在这里插入图片描述

这样代码看着有点冗余,这里可以写为三目表达式 此时再测试分析

在这里插入图片描述
在这里插入图片描述

第一个是右括号,直接进else取栈顶,但是当前栈为空,所以还需要特殊处理,调用STTop会出现断言报错

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此情况也推理符合 最后完整代码如下:

总结

以上就是深入解析数据结构之栈,下一篇内容为栈和队列,喜欢的兄弟们不要忘记一键三连支持一下

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、栈
    • 1.1 概念与结构
    • 1.2 栈的实现
      • 1.2.1 初始化
      • 1.2.2 销毁
      • 1.2.3 入栈
      • 1.2.4 判断栈是否为空
      • 1.2.5 出栈
      • 1.2.6 取栈顶元素
      • 1.2.7 获取栈中有效元素个数
  • 二、完整源码
  • 三、栈相关算法题
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档