当前位置: 首页 > backend >正文

C语言:栈的实现和剖析

目录

引言

一、栈的基本概念

1.1、栈的定义

1.2、相关概念

二、栈的实现

2.1、栈的基本结构

2.2、初始化栈

2.3、入栈

2.4、判断栈是否为空

2.5、出栈

2.6、取栈顶元素

2.7、获取栈中有效元素的个数

2.8、栈的销毁

三、练习:括号匹配问题

结语


引言

在数据结构的世界里,栈(Stack)以其独特的“后进先出”(LIFO)特性,成为一个实用且重要的线性结构。它无处不在,从函数调用的内部机制到表达式求值,再到浏览器的前进/后退功能,栈的身影随处可见。

本篇博客,我们将深入C语言的底层,亲手构建并剖析栈的实现。我们将探讨如何在C语言中灵活运用数组或链表来抽象出栈的本质操作——压栈(Push)和弹栈(Pop),并理解其背后的原理与细节。无论你是数据结构初学者,还是希望巩固C语言编程技能,相信本篇都能为你呈现一个清晰、生动的栈实现之旅。

一、栈的基本概念

1.1、栈的定义

栈是一种特殊的线性表,只允许在固定的一端进行插入和删除操作。

栈就是先进后出,后进先出

1.2、相关概念

栈顶:进行数据插入或删除操作的一端

栈底:不进行数据插入或删除操作的一端

压栈:栈的插入操作,也叫入栈,进栈

出栈:栈的删除操作

二、栈的实现

2.1、栈的基本结构

前面说了,栈是一种线性表,而目前我们所学的线性表只有顺序表和链表,我们应该用那种数据结构去实现栈呢?其实两者都可以,但是大部分的栈还是由顺序表实现的

下面就是用顺序表定义栈结构:

//定义栈的结构
typedef int StackDataType;
typedef struct Stack
{StackDataType* arr;    //顺序表int top;               //栈顶(元素个数)int capacity;          //容量
}Stack;

这里的成员 top 是指栈的栈顶元素的下标+1 ,同时也是栈的元素个数的含义。

2.2、初始化栈

栈的初始化很简单,就是给栈的结构成员赋一个初始值,当然,top 和 capacity就赋0了,arr指针是赋NULL了

void StackInit(Stack* ps)
{ps->arr = NULL;ps->top = ps->capacity = 0;
}

2.3、入栈

这里放一个设定,入栈和出栈都是在顺序表的末尾进行。(当然也可以设置成顺序表的头部,只要你不嫌麻烦)

入栈需要先判断空间容量是否足够?不够,扩容,然后在入栈

void StackPush(Stack* ps, StackDataType x)
{assert(ps);//判断容量是否足够?if (ps->top == ps->capacity){//扩容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;StackDataType* tmd = (StackDataType*)realloc(ps->arr ,sizeof(StackDataType) * newCapacity);if (tmd == NULL){perror("malloc fail!");exit(1);}ps->arr = tmd;ps->capacity = newCapacity;}//入队ps->arr[ps->top++] = x;
}

注意扩容还要保留原数据,所以用realloc! 

2.4、判断栈是否为空

判断栈是否为空非常简单,就看栈中的个数为不为0就好了

bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}

2.5、出栈

出栈和顺序表一样,只用 size--就可以了,后面插入数据会自己覆盖的。别忘了要判断栈是否为空

//出栈
void StackPop(Stack* ps)
{assert(!StackEmpty(ps));ps->top--;
}

2.6、取栈顶元素

刚才说的是出栈,而现在是取栈顶元素,二者是不一样的,但经常搭配使用。

和出栈一样,栈里面有东西才能返回,因此要判断栈是否为空

StackDataType StackTop(Stack* ps)
{assert(!StackEMpty(ps));return ps->arr[ps->top - 1];
}

2.7、获取栈中有效元素的个数

这个直接把个数返回就好了

int StackSize(Stack* ps)
{assert(ps);return ps->top;
}

2.8、栈的销毁

需要注意的是,栈中的数组是通过malloc开辟空间,最后应该用 free 释放,但是,为了防止为初始化的栈销毁, 所以应该先判断一下

void StackDestory(Stack* ps)
{assert(ps);if(ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}

三、练习:括号匹配问题

20. 有效的括号 - 力扣(LeetCode)

这道题就需要用到栈,遍历该数组,遇见左符号就入栈,遇见右符号就出栈,并一直匹配,直到栈为空,如果遍历完,栈不为空,那就返回不匹配。

像上述动图那样,遍历完数组后栈内还有剩余,那么就返回 false ,同样,如果中途符号不匹配,那么也是返回 false ,其余情况返回 ture

具体代码 如下:

bool isValid(char* s) 
{int length = strlen(s);if(length % 2 != 0){return false;}//创建一个栈Stack ps;StackInit(&ps);//遍历字符串,将每个字符串压入栈中char* s2 = s;while(*s2 != '\0' ){//是左边字符就入栈,否则就出栈比较if(*s2 == '(' || *s2 == '{' || *s2 == '['){StackPush(&ps , *s2);}else{if(ps.top == 0){return false;}SDataType ch = StackTop(&ps);StackPop(&ps);//如果if(*s2 ==')' && ch != '(' ||*s2 == '}' && ch != '{' ||*s2 == ']' &&ch != '['){return false;}}//判断完成后s2++;}//循环完后,还要检查栈是否为空,如果不为空,返回fasleif(ps.top != 0){StackDestory(&ps);return false;}StackDestory(&ps);return true;
}

结语

恭喜你已经学完了栈的全部内容,如果有什么问题欢迎私信我或者评论区见哦。

http://www.xdnf.cn/news/17051.html

相关文章:

  • css怪异模式(Quirks Mode)和标准模式(Standards Mode)最明显的区别
  • 【Java String】类深度解析:从原理到高效使用技巧
  • 软件架构:系统结构的顶层设计与战略约束
  • webrtc弱网-OveruseFrameDetector源码分析与算法原理
  • C++ 类和对象(1)
  • 【qt5_study】1.Hello world
  • SpringCloud学习------Hystrix详解
  • 奇偶校验码原理与FPGA实现
  • ubuntu自动重启BUG排查指南
  • Android 性能基准测试(Benchmark)完全指南:专业方法与最佳实践
  • 【RK3576】【Android14】Uboot下fastboot命令支持
  • 磁悬浮转子振动控制:主动电磁力如何成为高速旋转的“振动克星”
  • 基于Java AI(人工智能)生成末日题材的实践
  • 【docker】UnionFS联合操作系统
  • 《Linux编译器:gcc/g++食用指南》
  • 面试题:前端权限设计
  • # Kafka 消费堆积:从现象到解决的全链路分析
  • Spring小细节
  • lesson32:Pygame模块详解:从入门到实战的2D游戏开发指南
  • 关于为什么ctrl c退不出来SecureCRT命令行的原因及其解决方法:
  • 【25-cv-23395】宠物/婴儿玩具品牌BESTSKY商标维权!
  • MinIO02-Docker安装
  • STM32内部读写FLASH
  • “Why“比“How“更重要:层叠样式表CSS
  • 计算机网络:详解路由器如何转发子网数据包
  • MySQL 查询性能优化与索引失效问题全解析
  • 需求测试用例设计
  • 落霞归雁:从自然之道到“存内计算”——用算法思维在芯片里开一条“数据高速航道”
  • Vue3核心语法进阶(Props)
  • 【C# Winform】 Action事件驱动的多层数据传递