0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是顺序栈?什么又是链栈?

电子工程师 来源:编程学习总站 作者:写代码的牛顿 2021-06-15 10:50 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1、顺序栈

栈是一种后进先出的数据结构,栈的实现方式主要有2种,顺序栈和链栈。顺序栈则是栈的元素虚拟内存地址是连续的,链栈则是栈元素虚拟地址非连续的。在C语言里数组的元素虚拟地址是连续的但是数组大小必须在编译的时候确定,用于实现栈不够灵活。而在C语言里调用malloc申请到的一块内存虚拟地址是连续的,而且大小在运行期间确定,比较符合我们灵活的实现顺序栈的需求。先来看一下顺序栈的定义和函数声明。

#define NAN (0xFFFFFFFE) typedef struct stack{ int size; int cap; int front; int *arr; }_stack_t; extern void stack_init(_stack_t *s, int capacity); //初始化栈 extern void stack_push(_stack_t *s, int data); //入栈 extern int stack_pop(_stack_t *s); //出栈 extern int stack_size(_stack_t *s); //获取栈大小 extern bool stack_is_empty(_stack_t *s); //判断栈是否为空 extern bool stack_is_full(_stack_t *s); //判断栈是否满 extern void stack_destroy(_stack_t *s); //销毁栈

这里我们自定义了一个_stack_t类型,size是栈大小,cap是栈容量,front是栈顶,arr指针指向一块存储数据的内存,我们还通过宏定义了一个NAN值表示非法值。

栈初始化

函数实现如下:

void stack_init(_stack_t *s, int capacity){ if(s == NULL || capacity 《= 0){ return; } s-》arr = (int *)malloc(capacity * sizeof(int)); s-》front = 0; s-》size = 0; s-》cap = capacity; }

这里我们申请了一块内存用于存储数据,并保存栈容量大小。

入栈

函数实现如下:

void stack_push(_stack_t *s, int data){ if(s == NULL){ return; } if(stack_is_full(s)){ return; } s-》size++; //栈使用大小增1 s-》arr[s-》front++] = data; //保存数据后栈顶指针往后移 }

由于栈容量有限,每次将数据压入栈之前先判断一下栈是否满,栈未满才能继续往里压入数据。

出栈

每次出栈是后面入栈的数据先出,前面入栈的数据后出。函数实现如下:

int stack_pop(_stack_t *s){ if(s == NULL){ return NAN; } //判断栈是否空 if(stack_is_empty(s)){ return NAN; } s-》size--; //栈使用量减1 return s-》arr[--s-》front]; //先递减栈顶指针,获取栈顶数据 }

栈为空时说明栈里没有数据则返回一个非法值,否则获取栈顶数据并返回。

获取栈大小

函数实现如下:

int stack_size(_stack_t *s){ if(s == NULL){ return 0; } return s-》size; }

判断栈是否为空

函数实现如下:

bool stack_is_empty(_stack_t *s){ if(s == NULL){ return true; } return s-》size 》 0 ? false : true; }

判断栈是否满

函数实现如下:

bool stack_is_full(_stack_t *s){ if(s == NULL){ return false; } return s-》size == s-》cap ? true : false; }

销毁栈

函数实现如下:

void stack_destroy(_stack_t *s){ if(s == NULL){ return; } if(s-》arr){ free(s-》arr); } s-》arr = NULL; s-》cap = 0; s-》size = 0; s-》front = 0; }

销毁栈操作主要是释放内存,并初始化成员变量。

2、链栈

在前面的文章中我们讲解了单链表,在文中我们采用头插法插入结点到链表,由于头插法每次将最新的数据插入到链表头,如果依次遍历链表获取链表结点的数据,就是标准的栈弹出数据的操作。现在我们用前面文章实现的单链表实现一个链栈,顾名思义链栈就是用链式数据结构实现栈。我们自定义一个栈数据类型并声明一些操作函数。

typedef list_t stack_linked_t; //自定义栈数据类型 extern stack_linked_t *new_stack_linked_node(int data); //新建一个栈结点 extern void stack_linked_push(stack_linked_t **s, int data); //入栈 extern int stack_linked_pop(stack_linked_t **s); //出栈 extern int stack_linked_size(stack_linked_t *s); //获取栈大小 extern bool stack_linked_is_empty(stack_linked_t *s); //判断栈是否为空 extern void stack_linked_destroy(stack_linked_t **s); //销毁栈

这里我们将list_t自定义成stack_linked_t,看似多此一举,实际上更直观了,我们虽然使用链表实现栈,但是如果将数据类型声明为list_t反而更迷惑。由于链栈是链式结构不存在栈是否满的情况,除非已经无法申请到内存。

新建栈结点

函数实现如下:

stack_linked_t *new_stack_linked_node(int data){ return new_list_node(data); }

这里我们直接对新建链表结点函数进行封装,后面我们也会大量用到链表操作函数,差不多都是类似的封装。

入栈

函数实现如下:

void stack_linked_push(stack_linked_t **s, int data){ //这里一定要注意分开两个if,因为或运算符的特性 if(s == NULL){ return; } if(*s == NULL){ return; } //采用头插法插入链表 *s = list_add(*s, data); }

这里重点注意由于我们传入的是一个二级指针if(s == NULL)和if(*s == NULL)一定要分开处理,不能使用||运算进行处理,因为||运算符会执行第二个判断,如果s == NULL成立那么在执行第二个判断时由于使用了空指针程序会奔溃。

出栈

为了获取链表头结点,我们定义了一个获取链表头结点函数,函数实现如下:

list_t *get_list_head(list_t **list){ if(list == NULL){ return NULL; } if(*list == NULL){ return NULL; } list_t *head = *list; //链表只有一个结点 if((*list)-》next == NULL){ *list = NULL; return head; } //链表长度大于1则保存头结点,新头结点是原头结点的下一个结点 *list = (*list)-》next; head-》next = NULL; //原头结点一定要将next指针置为NULL return head; }

出栈函数实现如下:

int stack_linked_pop(stack_linked_t **s){ //这里一定要注意分开两个if,因为或运算符的特性 if(s == NULL){ return NAN; } if(*s == NULL){ return NAN; } stack_linked_t *stack_node = get_list_head(s); int data = stack_node-》data; free(stack_node); return data; }

获取链表头结点数据并释放内存。

获取栈大小

获取栈大小其实就是获取链表长度,因此我们定义了一个获取链表长度函数,函数实现如下:

//获取链表长度 int list_length(list_t *list){ if(list == NULL){ return 0; } int length = 0; while(list != NULL){ length++; list = list-》next; } return length; }

获取栈大小实现函数如下:

int stack_linked_size(stack_linked_t *s){ if(s == NULL){ return 0; } return list_length(s); }

判断栈是否为空

函数实现如下:

bool stack_linked_is_empty(stack_linked_t *s){ if(s == NULL){ return true; } return list_length(s) 》 0 ? false : true; }

链表长度为0则链表为空,非0则有数据。

销毁栈

函数实现如下:

void stack_linked_destroy(stack_linked_t **s){ if(s == NULL){ return; } if(*s == NULL){ return; } list_destroy(*s); *s = NULL; }

3、验证测试

最后我们写一个小程序验证一下我们实现的栈是否正确,代码如下:

#include 《stdio.h》 #include “stack.h” int main() { _stack_t my_stack; int i = 0; stack_init(&my_stack, 5); //初始化栈 printf(“进栈顺序 ”); for(i = 0; i 《 5; i++){ printf(“%d, ”, i); stack_push(&my_stack, i); //将数据压入栈 } printf(“ ”); if(stack_is_full(&my_stack)){ printf(“栈已满 ”); }else{ printf(“栈未满 ”); } printf(“栈的大小是:%d ”, stack_size(&my_stack)); printf(“出栈顺序是 ”); for(i = 0; i 《 5; i++){ printf(“%d ,”, stack_pop(&my_stack)); } printf(“ ”); if(stack_is_empty(&my_stack)){ printf(“栈为空 ”); }else{ printf(“栈未空 ”); } stack_destroy(&my_stack); //销毁栈 printf(“ ”); printf(“用链表实现栈 ”); printf(“入栈顺序 ”); printf(“%d ,”, 0); stack_linked_t *my_stack2 = new_stack_linked_node(0); for(i = 0; i 《 5; i++){ printf(“%d ,”, i + 1); stack_linked_push(&my_stack2, i + 1); } printf(“ ”); printf(“栈大小是: %d ”, stack_linked_size(my_stack2)); printf(“出栈顺序是 ”); for(i = 0; i 《 6; i++){ printf(“%d ,”, stack_linked_pop(&my_stack2)); } printf(“ ”); if(stack_linked_is_empty(my_stack2)){ printf(“链栈为空 ”); }else{ printf(“链栈非空 ”); } stack_linked_destroy(&my_stack2); return 0; }

编译运行输出:

进栈顺序 0, 1, 2, 3, 4, 栈已满 栈的大小是:5 出栈顺序是 4 ,3 ,2 ,1 ,0 , 栈为空 用链表实现栈 入栈顺序 0 ,1 ,2 ,3 ,4 ,5 , 栈大小是: 6 出栈顺序是 5 ,4 ,3 ,2 ,1 ,0 , 链栈为空

输出完全正确。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    183

    文章

    7642

    浏览量

    144664

原文标题:数据结构与算法篇-栈

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Stack到底用来干嘛的呢?

    Stack_Size就是大小,0x00000400就是代表有1K(0x400/1024)的大小。 那这个到底用来干嘛的呢? 比如说我们函数的形参、以及函数里定义的局部变量就是存储在里,所以
    发表于 12-01 08:04

    堆和的区别

    一个由C/C 编译的程序占用的内存分为以下几个部分: 区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的。 堆区(heap):一般由
    的头像 发表于 11-27 18:13 919次阅读

    在Keil5中查看大小

    1、修改启动文件: 方法说明:大小通常在启动文件中定义。可以通过直接修改这个文件中的Stack_Size变量来调整大小。 操作步骤:找到对应的启动文件,定位到Stack_Size的定义处,修改
    发表于 11-14 06:32

    告别 “溢出”!用 RT-Trace 工具精准定位嵌入式系统内存隐患 | 技术集结

    目录前言好物直达测试环境使用方法和效果总结前言相信无论大佬还是小白,都在开发中遇到过溢出的问题,而且因为没有明确日志,难以定位问题的根源。StackOverflow社区的命名也由此而生,而到现在
    的头像 发表于 08-31 09:34 911次阅读
    告别 “<b class='flag-5'>栈</b>溢出”!用 RT-Trace 工具精准定位嵌入式系统内存隐患 | 技术集结

    自动驾驶中常提的“全”是个啥?有必要“全”吗?

    [首发于智驾最前沿微信公众号]随着自动驾驶技术落地,越来越多车企公布了自己的自动驾驶方案,在很多车企的宣传中,会使用“全自研”的说法来证明自己的实力。所谓“全”,字面意思是全套技术的自主开发
    的头像 发表于 08-27 09:43 674次阅读
    自动驾驶中常提的“全<b class='flag-5'>栈</b>”是个啥?有必要“全<b class='flag-5'>栈</b>”吗?

    黑芝麻智能AI全机器人计算平台荣膺国际大奖

    黑芝麻智能AI全机器人计算平台荣膺新加坡年度"GO! Technology Utilisation Winner",作为面向新一代机器人实时AI推理打造的全计算平台,该方案已成功部署于清洁、巡检及移动机器人平台。
    的头像 发表于 08-07 17:35 1747次阅读

    AI应用创新与全技术融合分论坛即将召开

    2025开放原子开源生态大会即将启幕,其中 “AI应用创新与全技术融合分论坛”将于 7月24日重磅亮相。论坛聚焦人工智能技术与开源生态的深度融合,邀请各领域用户、技术专家、开发者分享AI应用创新实践,旨在探索AI技术从底层算力到AI应用场景的全创新路径,为行业带来一场
    的头像 发表于 07-23 09:54 723次阅读

    龙芯中科全自主打造安全存储生态

    数据存储是现代信息基础设施的核心支柱,其自主可控能力关乎国家数字安全。作为国内唯一具备全自主调优能力的企业,龙芯中科依托从底层硬件到上层软件的完整技术,强势布局存储领域,全面覆盖分布式存储、集中式存储、灾备系统等主流市场需求。
    的头像 发表于 07-05 16:49 1527次阅读

    移远通信携手高通:以全车载解决方案,共绘智能出行新蓝图

    6月26日至27日,2025高通汽车技术与合作峰会于苏州盛大举办。本次峰会以“我们一起,行稳智远”为主题,全方位呈现智能汽车全技术、全产业生态与全场景体验。作为高通长期稳定的战略合作伙伴,移远
    的头像 发表于 06-27 20:35 795次阅读
    移远通信携手高通:以全<b class='flag-5'>栈</b>车载解决方案,共绘智能出行新蓝图

    研发排查问题的利器:一款方法调用跟踪工具

    作者:京东物流 郭忠强 导语 本文从日常值班问题排查痛点出发,分析方法复用的调用路和上下文业务逻辑,通过思考分析,借助帧开发了一个方法调用的链式跟踪工具,便于展示一次请求的方法串行调用
    的头像 发表于 05-06 17:24 3018次阅读
    研发排查问题的利器:一款方法调用<b class='flag-5'>栈</b>跟踪工具

    深入浅出解析低功耗蓝牙协议

    Bluetooth LE协议为什么要分层?怎么理解Bluetooth LE“连接”?如果Bluetooth LE协议只有ATT层没有GATT层会发生什么? 一、协议框架 一般而言,我们把某个
    的头像 发表于 04-09 14:49 1020次阅读
    深入浅出解析低功耗蓝牙协议<b class='flag-5'>栈</b>

    三种蓝牙架构实现方案(蓝牙协议方案)

    蓝牙架构实现方案有哪几种?我们一般把整个蓝牙实现方案叫做蓝牙协议,因此这个问题也可以这么阐述:蓝牙协议有哪些具体的架构方案?在蓝牙协议中,host是什么?controller是什么?HCI
    的头像 发表于 04-08 15:35 1221次阅读
    三种蓝牙架构实现方案(蓝牙协议<b class='flag-5'>栈</b>方案)

    智存无界 全智能 | 德明利邀您共聚CFMS 2025!

    3月12日,2025中国闪存市场峰即将启幕,本届峰会齐聚全球存储产业及核心应用企业,共同探讨技术创新与产业变革。届时,德明利将展示全式智能存储矩阵和场景化解决方案,聚焦客户多元化需求,构建从芯片
    的头像 发表于 02-27 17:15 670次阅读
    智存无界 全<b class='flag-5'>栈</b>智能 | 德明利邀您共聚CFMS 2025!

    商汤科技领跑2024年中国GenAI技术市场

    创新实力强、应用落地广,GenAI(生成式AI)技术领域,商汤科技位居国内榜首!
    的头像 发表于 12-27 16:07 1062次阅读

    曙光云开启全智能时代

    近日,“全可信 云中生智”曙光云战略发布会召开。曙光云从首创“城市云”进化到实现“全智能云”,打造“云智、云安、云算、云数”四位一体能力体系,深度赋能千行百业数智化转型升级。
    的头像 发表于 12-19 15:11 918次阅读