小白都能看懂的 “栈”

news2024/12/22 23:11:11

什么是栈?首先引用维基百科的解释:

栈(stack)是计算机科学中的一种抽象资料类型,只允许在有序的线性资料集合的一端(称为堆栈顶端,top)进行加入数据(push)和移除数据(pop)的运算。因而按照后进先出(LIFO, Last In First Out)的原理运作。

有点难懂?没关系,我们来看一张图: 

没错,桌上的这一叠盘子就是一个栈结构(实际上还需要加一些限定条件,一会会讲到)。其实我们每天都会与“栈”接触。可以说只要理解了这一叠盘子,你就理解了栈!

首先根据维基百科的专业定义,栈有两种基本操作:弹栈和压栈:

图片

对应到我们的这一叠盘子,通常情况下也是两个操作:取盘子和放盘子:

图片

还记得我们上文说过这一叠盘子如果要成为栈,还需要一些限定条件吗?下面我们就来看一下这个条件:

上述对盘子的所有操作必须在盘子顶进行!

说得更直白点,就是取盘子的时候只能从顶部取,不能从中间取;放盘子的时候只能放在顶部,不能插到中间。

于是我们发现,我们每次从这一叠盘子里取出的盘子都是最后叠上去的,最先叠上去的只有在上面的盘子全部取完以后才能取出来。而上面这句话,就是栈结构的最大特点:

后进先出(LIFO, Last In First Out)

OK。以上就是认识栈结构所需要的所有知识,是不是很简单。

图片

下面我们再用一组图片深入理解一下栈结构与其操作过程:

图片

这是一个单向开口的空间,所有元素都通过顶部的开口进入和弹出,内部的橙色方块就是进栈的元素。很明显,先进入的元素会被后进入的元素盖住,取数据的时候只能先取后进入的数据。此时最上面的元素C就被称之为栈顶,最下面的元素A自然就成为了栈底

图片

这就是压栈过程,新放入的元素D放在栈的最上面,代替原来的元素C成为了新的栈顶。栈底仍然为A。

图片

这是弹栈(出栈)过程,元素D被取出,元素C再次成为栈顶,元素A依然是栈底,并且只有等到A上面的所有元素(B、C)全部取出时,A才有机会出栈。

图片

除了压栈和弹栈这两个基本操作,栈结构还有两个特殊状态。此处停止下滑,请思考一下是哪两个。

图片

左边的栈中没有任何元素,所以被称之为空栈,右边被填满了元素,因此被称之为满栈。一般情况下,在程序初始化栈的时候要保证栈为空,在后续的压栈与弹栈操作流程中还要在每次操作前检测栈状态,避免在满栈状态下压栈,在空栈的状态下弹栈,一旦出现上面两种情况,就会导致数据溢出或访问到非法数据,轻则导致程序崩溃死机,重则引起设备失控,甚至出现伤人事件!这并不是危言耸听,试想如果一架运行着的飞机突然自动控制单元出现了栈溢出,导致控制程序死机,无法被控制,也无法切换手动运行。后果自然是非常严重的!

说了这么多,好像栈是一个很简单,且很不灵活的结构,那这玩意儿到底有啥用?

图片

可不要小看了这个结构,在如今的计算机科学、编程与算法中,栈是非常重要,也是用得非常多的一种数据结构,下面我们举几个例子:

  • 函数调用:在计算机程序中,函数的调用和返回借助栈来实现。每次调用函数时,函数的参数和局部变量都会被存储在栈中,当函数执行完成后,栈会弹出这些数据,返回调用点。

  • 表达式求值:栈可以用于解析和求值表达式,包括中缀表达式转换为后缀表达式以及后缀表达式的求值。

  • 内存管理:栈用于存储函数的局部变量和临时数据,对内存的分配和释放起到了关键作用。

  • 括号匹配:栈可以用于检查括号匹配,例如检查一个字符串中的括号是否正确闭合。

  • 后退和撤销:在许多应用程序中,栈可以用于实现后退和撤销功能,例如文本编辑器中的撤销操作。

上面这些例子都是我们日常生活中频繁遇到的场景,由此可见栈无处不在。比如就在写这段话的时候,我还撤销了一个错别字。

图片

到这里你以为就结束了吗?NO! NO! NO! 作为实践主义的一员,我深信实践是检验真理的唯一标准,没有实践,怎么能说理解!接下来,我们一起来用 C 语言实现一个栈结构!

图片

发车之前,我们先明确一点,栈在软件上一般有数组和链表两种实现方式,链表形式会比较复杂,并且涉及到另外的数据结构,本文既然是讲栈的,就尽量不引入其他结构来避免理解困难,因此下文会实现一个纯数组栈。

首先我们来定义一个最简单的栈结构:

#define MAX_SIZE 100  // 栈的最大容量

typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

可以看到,这个结构里有一个数组和一个变量。其中 data 数组就是栈容器,用于存储数据,而 top 变量就用于指示当前栈顶的栈顶计数(有些地方会用一个指针,即栈顶指针。这里直接用计数比较好理解,作用是一样的)。

定义完结构后我们来进行初始化:

// 初始化栈
void init(Stack *stack) { stack->top = -1; }

这里的初始化逻辑很简单,就是将 top 变量的值设置为 -1 。在后续的程序中只要这个值为 -1,我们就认为当前栈为空:

// 判断栈是否为空
bool isEmpty(Stack *stack) { return stack->top == -1; }

自然,当 top 值为 MAX_SIZE - 1 时,就代表当前栈已满(有一个元素时值为0):

// 判断栈是否已满
bool isFull(Stack *stack) { return stack->top == MAX_SIZE - 1; }

压栈:


// 压栈
void push(Stack *stack, int value) {
    if (isFull(stack)) {
        printf("栈已满,无法入栈\n");
    } else {
        stack->data[++stack->top] = value;
    }
}

将数据压栈就是把数据放入数组,同时栈顶计数加一表示当前加入了一个数据。这里要注意,压入数据之前必须要检查栈是否满,避免栈溢出。

弹栈(出栈):

// 出栈
int pop(Stack *stack, int *value) {
    if (isEmpty(stack)) {
        printf("栈已空,无法出栈\n");
        return -1;
    } else {
        *value = stack->data[stack->top--];
        return 0;
    }
}

弹栈则是对外弹出(返回)数组内有效数据的顶部数据,同时,栈顶计数减一表示当前取出了一个数据。同样,必须确保栈不为空再进行弹栈操作。

查看栈顶元素:

// 获取栈顶元素
int top(Stack *stack, int *value) {
    if (isEmpty(stack)) {
        printf("栈已空,无栈顶元素\n");
        return -1;
    } else {
        *value = stack->data[stack->top];
        return 0;
    }
}

该操作和弹栈很类似,但必须要注意,该操作不会导致栈顶计数的变化,只是查看元素。可以理解为,你小时候每次走过玩具店都要看一眼橱窗,然后对妈妈说:我就看看,不买~

打印栈中的元素:

// 打印栈中的元素
void printStack(Stack *stack) {
    printf("栈中的元素为:");
    for (int i = 0; i <= stack->top; i++) {
        printf("%d ", stack->data[i]);
    }
    printf("\n");
}

调试接口,从栈顶到栈底依次打印出栈内数据,用于查看栈内的数据状态。

最后我们用一个 main 函数将上述接口都串起来,形成一个可以运行的程序:

int main() {
    int ret = -1, value = 0;
    Stack stack;
    init(&stack);
    push(&stack, 3);
    push(&stack, 5);
    push(&stack, 7);
    printStack(&stack);
    ret = pop(&stack, &value);
    if (ret == 0) {
        printf("出栈元素为:%d\n", value);
    } else {
        printf("出栈失败");
    }
    printStack(&stack);

    return 0;
}

这个程序的运行逻辑如下:

定义一个栈 > 初始化栈 > 将3压入栈 > 将5压入栈 > 将7压入栈 > 打印当前栈状态 > 弹栈 > 打印弹出数据 > 打印当前栈状态。

这里再次停止往下滑,先思考一下输出结果会是什么。下面来看下运行结果:

jay@jaylinuxlenovo:~/test/stack$ ./stack 
栈中的元素为:3 5 7 
出栈元素为:7
栈中的元素为:3 5 

是不是和你想的一样呢?对于栈的概念,你是不是真的理解了呢。下面附上完整代码,感兴趣的小伙伴可以自己尝试运行一下。

/***************************************************************
 * @file           stack.c
 * @brief
 * @author         WKJay
 * @Version
 * @Date           2023-12-07
 ***************************************************************/
#include <stdio.h>
#include <stdbool.h>

#define MAX_SIZE 100  // 栈的最大容量

typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

// 初始化栈
void init(Stack *stack) { stack->top = -1; }

// 判断栈是否为空
bool isEmpty(Stack *stack) { return stack->top == -1; }

// 判断栈是否已满
bool isFull(Stack *stack) { return stack->top == MAX_SIZE - 1; }

// 压栈
void push(Stack *stack, int value) {
    if (isFull(stack)) {
        printf("栈已满,无法入栈\n");
    } else {
        stack->data[++stack->top] = value;
    }
}

// 出栈
int pop(Stack *stack, int *value) {
    if (isEmpty(stack)) {
        printf("栈已空,无法出栈\n");
        return -1;
    } else {
        *value = stack->data[stack->top--];
        return 0;
    }
}

// 获取栈顶元素
int top(Stack *stack, int *value) {
    if (isEmpty(stack)) {
        printf("栈已空,无栈顶元素\n");
        return -1;
    } else {
        *value = stack->data[stack->top];
        return 0;
    }
}

// 打印栈中的元素
void printStack(Stack *stack) {
    printf("栈中的元素为:");
    for (int i = 0; i <= stack->top; i++) {
        printf("%d ", stack->data[i]);
    }
    printf("\n");
}

int main() {
    int ret = -1, value = 0;
    Stack stack;
    init(&stack);
    push(&stack, 3);
    push(&stack, 5);
    push(&stack, 7);
    printStack(&stack);
    ret = pop(&stack, &value);
    if (ret == 0) {
        printf("出栈元素为:%d\n", value);
    } else {
        printf("出栈失败");
    }
    printStack(&stack);

    return 0;
}

到站,下车!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1821904.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PMP考试新考纲以及题型有哪些变化?

一、PMP简介 PMP&#xff08;Project Management Professional&#xff09;是项目管理协会&#xff08;PMI&#xff09;在全球范围内推出的项目经理资格认证。获得PMP证书不仅可以提升项目经理的管理水平&#xff0c;还能直接展现个人竞争力&#xff0c;是项目管理专业人士身份…

4S店试驾线上预约小程序源码系统 前后端分离 带完整的源代码包+安装部署教程

系统概述 这款 4S 店试驾线上预约小程序源码系统旨在为 4S 店和消费者提供便捷、高效的试驾预约服务。通过小程序&#xff0c;消费者可以轻松预约试驾&#xff0c;4S 店可以方便地管理预约信息&#xff0c;提高工作效率和服务质量。 代码示例 系统特色功能一览 1.便捷的预约流…

【乐吾乐2D可视化组态编辑器】下载离线部署包

下载离线部署包 乐吾乐2D可视化/大屏可视化支持导出为可直接部署运行的html文件。 乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 1. 从“文件”菜单选择“下载离线部署包” 【提示】离线部署包是需要付费下载&#xff0c;首次体验&#xff0c;可以选择&…

面向对象编程重载

系列文章目录 文章目录 系列文章目录前言一、重载&#xff08;overload&#xff09; 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了…

今年的就业环境不容乐观,你想好怎么应对了吗

今年的就业环境不容乐观&#xff0c;你想好怎么应对了吗 毕业生进入职场的历程往往充满挑战和未知&#xff0c;尤其是在当前经济环境下&#xff0c;失业问题愈发凸显。本文通过分享几位年轻人的真实经历&#xff0c;剖析大学生及职场人士面临的困境&#xff0c;并提供应对策略…

体验亚马逊AIGC——Amazon Bedrock

前言 随着人工智能技术的不断发展&#xff0c;我们已经进入了一个全新的时代&#xff0c;即AI驱动的时代。在这个时代&#xff0c;人工智能已经逐渐成为我们生活中不可或缺的一部分&#xff0c;它可以帮助我们更好地处理各种复杂的问题&#xff0c;提高我们的工作效率&#xff…

Intellij IDEA开发Android项目打包生成APK

在 IntelliJ IDEA 左上方中选择 “Build” -> “Generate Signed Bundle / APK…”选择“APK”——“Next”——“Create New…”&#xff08;Password随便填123456即可&#xff09; “Next”——选择release&#xff08;APK生成后默认存放在本项目的release文件夹里&#x…

《Nest系列 - 1. 运行一个Nest项目以及整体目录学习》

初识Nest心路历程 作为一名前端开发&#xff0c;说实话&#xff0c;学习Nest后端技术, 会有一定的成本。我试着阅读文档&#xff0c;安装项目&#xff0c;把项目跑起来&#xff0c; 当我看到久违的Hellow world 后&#xff0c;还来不及欣喜&#xff0c;就困惑了, 作为一个后端…

探索Jetpack Compose中的高效导航库:Voyager项目

探索Jetpack Compose中的高效导航库&#xff1a;Voyager项目 在Jetpack Compose中实现高效、可扩展的导航是每个开发者的追求。Voyager作为一个多平台导航库&#xff0c;不仅与Jetpack Compose无缝集成&#xff0c;还提供了一套务实的API&#xff0c;帮助开发者创建单活动应用…

零基础入门学用Arduino 第二部分(二)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

【仿真建模-anylogic】FlowchartPort原理解析

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-14 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 类图 2. 原理解析 2.1 核心函数 FlowchartPort继承Port类&#xff0c;并定义了一系列抽象函数&#xff1b;核心函数如下&#xff1…

基于BP神经网络对鸢尾花数据集分类

目录 1. 作者介绍2. 关于理论方面的知识介绍2.1 BP神经网络原理2.2 BP神经网络结构 3. 关于实验过程的介绍&#xff0c;完整实验代码&#xff0c;测试结果3.1 鸢尾花数据集介绍3.2 代码演示3.3 结果演示 4. 问题与分析 1. 作者介绍 侯硕&#xff0c;男&#xff0c;西安工程大学…

智能合约之路:Web3时代的商业革新之道

随着区块链技术的日益成熟和普及&#xff0c;智能合约作为其重要应用之一&#xff0c;正逐渐引领着我们进入一个全新的商业时代&#xff0c;即Web3时代。在这个时代&#xff0c;智能合约不仅改变着商业交易的方式&#xff0c;更为商业模式带来了颠覆性的革新。本文将深入探讨智…

18.9k star!一个高性能的嵌入式分析型数据库,主要用于数据分析和数据处理任务

大家好&#xff0c;今天给大家分享的是一个开源的面向列的关系数据库管理系统(RDBMS)。 DuckDB是一个嵌入式的分析型数据库&#xff0c;它提供了高性能的数据分析和数据处理能力。DuckDB的设计目标是为数据科学家、分析师和数据工程师提供一个快速、灵活且易于使用的数据分析工…

从路边摊到五星级酒店:六西格玛培训的价格与品质探秘!

当我们深入探讨市面上的六西格玛培训价格差异时&#xff0c;确实会发现不同机构之间存在着显著的差别。以张驰咨询和xx机构为例&#xff0c;两者在价格定位上形成了鲜明的对比&#xff0c;同时也展示了不同机构在教学理念和服务品质上的不同。 xx机构之所以能以亲民的价格吸引…

同三维T80005JEHVA 4K视频解码器

同三维T80005JEHVA视频解码器 可解1路4K30HDMI/VGA/CVBS1路3.5音频 可解电台音频网络流&#xff0c;可同时解4个网络流&#xff0c;分割输出 可预设十个流&#xff0c;任意切换1路流输出 <!--[endif]----><!--[if !vml]--> <!--![endif]----> 介绍&…

反贿赂管理体系认证:提升企业诚信与防范风险的双重利器

反贿赂管理体系认证在当今商业环境中发挥着至关重要的作用。这一认证不仅有助于提高企业的道德标准和社会责任感&#xff0c;还能有效防范商业风险&#xff0c;并提升内部管理水平和工作效率。 反贿赂管理体系认证要求企业制定和执行严格的反贿赂政策和程序&#xff0c;从而在…

优思学院|做质量没有前途?10年质量人想对大家说...

你是否也有过这样的困惑&#xff1f;做质量工作究竟有没有前途&#xff1f;是不是感觉每天都在重复一样的事情&#xff0c;看不到未来的希望&#xff1f; 今天&#xff0c;优思学院分享一个任职于五百强企业、有着10年经验的质量人、六西格玛黑带学生徐某的文章&#xff0c;和…

投资策略如何降低风险?WeTrade众汇一分钟分享

通过投资不同的公司、行业甚至国家&#xff0c;投资策略涉及多元化投资&#xff0c;投资者可以平衡潜在的收益与风险&#xff0c;这确实是降低风险的一种常见方法。下面WeTrade众汇分享一种更现代的投资策略&#xff0c;将指数中所有工具的资本化纳入考量&#xff0c;确保不遗漏…

cdh中的zookeeper怎么配置zoo.cfg

你手动改了zoo.cfg目录是不会生效的&#xff0c;因为是cdh在管控&#xff0c;所以只能通过cdh修改。 首先打开cdh。 xxx:7180 点击zookeeper 选配置&#xff0c;然后选高级 在右边找&#xff0c;有一个就是zoo.cfg&#xff0c;可以点击右边的感叹号。然后在里面编辑的就会直…