首页 > 其他分享 >[数据启示录 02] 堆栈

[数据启示录 02] 堆栈

时间:2023-12-14 20:32:49浏览次数:19  
标签:02 入栈 元素 栈顶 运算符 启示录 堆栈 表达式

堆栈(stack)是一种基于后进先出(LIFO,Last In First Out)原则的数据结构。它模拟了现实生活中的堆栈,类似于一摞盘子或一堆书。

堆栈有两个基本操作:入栈(push)和出栈(pop)。

  1. 入栈(push):将新元素添加到堆栈的顶部。新元素成为当前堆栈的最上面一个元素。
  2. 出栈(pop):从堆栈的顶部移除最上面的元素,并返回该元素的值。

除了这两个基本操作外,堆栈还可以支持其他常用操作,例如:

  • 栈顶(top):获取堆栈的顶部元素,但不移除它。
  • 判空(isEmpty):检查堆栈是否为空。
  • 获取大小(size):获取堆栈中元素的数量。

实际上,堆栈可以通过数组或链表来实现。

使用数组实现的堆栈称为顺序堆栈(array-based stack)。在顺序堆栈中,数组的末尾被用作栈顶,每次入栈操作都会将元素放置在数组末尾,而出栈操作则会从数组末尾移除元素。

使用链表实现的堆栈称为链式堆栈(linked stack)。在链式堆栈中,每个节点包含一个元素和一个指向下一个节点的引用。入栈操作将在链表头部插入新节点,而出栈操作则会移除链表头部的节点。

堆栈在计算机科学中有广泛的应用。例如,在编程中,堆栈常用于函数调用的过程中,每当一个函数被调用时,其相关信息(如参数、局部变量等)都会被压入堆栈中,当函数执行完毕后,这些信息又会被弹出堆栈。这种方式使得程序可以追踪函数的嵌套调用,并正确恢复执行状态。

堆栈还被用于解决许多其他问题,如括号匹配、表达式求值、深度优先搜索算法、回溯算法等。其简单性和高效性使得堆栈成为一种重要的数据结构。

[数据启示录 02] 堆栈_入栈

[数据启示录 02] 堆栈_运算符_02

[数据启示录 02] 堆栈_运算符_03

堆栈的抽象数据描述

堆栈(stack)是一种抽象数据类型(ADT),用于描述具有后进先出(LIFO,Last In First Out)特性的数据结构。它定义了以下操作:

  1. 初始化(Initialize):创建一个空的堆栈。
  2. 入栈(Push):将一个新元素添加到堆栈的顶部。
  3. 出栈(Pop):从堆栈的顶部移除最上面的元素,并返回该元素的值。
  4. 栈顶(Top):获取堆栈的顶部元素,但不移除它。
  5. 判空(IsEmpty):检查堆栈是否为空。
  6. 获取大小(Size):获取堆栈中元素的数量。

这些操作定义了堆栈的基本行为和特点。使用这些操作,可以实现各种具体的堆栈实现,如基于数组或链表的实现。

[数据启示录 02] 堆栈_入栈_04

[数据启示录 02] 堆栈_入栈_05

[数据启示录 02] 堆栈_入栈_06

[例] 如果三个字符按ABC顺序压入堆栈

• ABC的所有排列都可能 是出栈的序列吗? • 可以产生CAB这样的序 列吗?

在递归的过程中,我们维护一个栈和一个指向原始字符序列的指针。如果当前栈顶元素与指针指向的元素相同,则可以将其出栈;否则,需要将指针指向的元素入栈。当原始字符序列中的所有元素都已经被入栈后,我们可以逐步将栈中的元素出栈,从而得到一种可能的出栈序列。

使用上述方法,我们可以得到所有可能的出栈序列。如果其中包含了以CAB为开头的序列,那么就说明CAB是一种可能的出栈序列。否则,就不能产生CAB这样的序列。

总结:按照ABC的顺序依次压入堆栈,其所有可能的出栈序列有6种,分别是ABC、ACB、BAC、BCA、CBA和CAB。因此,CAB是一种可能的出栈序列。

栈的顺序存储实现

[数据启示录 02] 堆栈_堆栈_07

[数据启示录 02] 堆栈_堆栈_08

#define MAXSIZE 100 // 定义栈的最大容量

typedef struct {
    ElementType data[MAXSIZE]; // 用数组存储栈元素
    int top; // 栈顶指针,指向当前栈顶元素的位置
} Stack;

// 初始化栈
void InitStack(Stack *S) {
    S->top = -1; // 初始化栈顶指针为-1,表示空栈
}

// 判断栈是否为空
int IsEmpty(Stack *S) {
    return (S->top == -1);
}

// 判断栈是否已满
int IsFull(Stack *S) {
    return (S->top == MAXSIZE - 1);
}

// 入栈操作
void Push(Stack *S, ElementType item) {
    if (IsFull(S)) {
        printf("Stack is full. Cannot push element %d.\n", item);
    } else {
        S->data[++(S->top)] = item;
    }
}

// 出栈操作
ElementType Pop(Stack *S) {
    if (IsEmpty(S)) {
        printf("Stack is empty. Cannot pop element.\n");
        return ERROR; // ERROR可以是一个预定义的错误值
    } else {
        return S->data[(S->top)--];
    }
}

// 获取栈顶元素
ElementType GetTop(Stack *S) {
    if (IsEmpty(S)) {
        printf("Stack is empty. No top element.\n");
        return ERROR;
    } else {
        return S->data[S->top];
    }
}

[数据启示录 02] 堆栈_运算符_09

[数据启示录 02] 堆栈_运算符_10

堆栈的链式存储实现

[数据启示录 02] 堆栈_运算符_11

[数据启示录 02] 堆栈_堆栈_12

typedef struct StackNode {
    ElementType data; // 数据域
    struct StackNode *next; // 指针域,指向下一个节点
} StackNode;

typedef struct {
    StackNode *top; // 栈顶指针,指向当前栈顶元素
} LinkedStack;

// 初始化栈
void InitStack(LinkedStack *S) {
    S->top = NULL; // 初始化栈顶指针为空,表示空栈
}

// 判断栈是否为空
int IsEmpty(LinkedStack *S) {
    return (S->top == NULL);
}

// 入栈操作
void Push(LinkedStack *S, ElementType item) {
    StackNode *newNode = (StackNode *)malloc(sizeof(StackNode)); // 创建新节点
    newNode->data = item; // 设置新节点的数据域为要入栈的元素
    newNode->next = S->top; // 将新节点插入到栈顶
    S->top = newNode; // 更新栈顶指针
}

// 出栈操作
ElementType Pop(LinkedStack *S) {
    if (IsEmpty(S)) {
        printf("Stack is empty. Cannot pop element.\n");
        return ERROR; // ERROR可以是一个预定义的错误值
    } else {
        StackNode *temp = S->top; // 保存当前栈顶节点
        ElementType item = temp->data; // 获取栈顶元素的值
        S->top = temp->next; // 更新栈顶指针
        free(temp); // 释放原栈顶节点
        return item;
    }
}

// 获取栈顶元素
ElementType GetTop(LinkedStack *S) {
    if (IsEmpty(S)) {
        printf("Stack is empty. No top element.\n");
        return ERROR;
    } else {
        return S->top->data;
    }
}

堆栈应用:表达式求值

当涉及到表达式求值时,我们可以考虑使用堆栈的应用。以下是一个更复杂的例子来演示如何使用堆栈进行中缀表达式的求值。

假设我们要求解的表达式为中缀表达式:(3 + 4) * 5 - 6 / 2

  1. 创建一个空栈和运算符优先级字典。
  2. 从左到右遍历中缀表达式中的每个元素。
  3. 如果当前元素是数字,则将其转换为整数并直接入栈。
  4. 如果当前元素是运算符,进行以下操作:
  • 如果栈为空或者栈顶元素是左括号"(",则将当前运算符入栈。
  • 如果栈不为空,并且当前运算符的优先级大于栈顶运算符的优先级,则将当前运算符入栈。
  • 如果栈不为空,并且当前运算符的优先级小于等于栈顶运算符的优先级,则弹出栈顶运算符并进行计算,并将计算结果入栈。重复此步骤直到满足条件,然后将当前运算符入栈。
  • 如果当前元素是右括号")",则弹出栈顶运算符并进行计算,直到遇到左括号"("。左括号"("从栈中弹出,但不进行计算。
  1. 当遍历完中缀表达式后,将栈中剩余的运算符依次弹出并进行计算。
  2. 栈中仅剩下一个元素,即为表达式的计算结果。

以下是对中缀表达式(3 + 4) * 5 - 6 / 2求值的具体步骤:

  1. 创建一个空栈和运算符优先级字典(加法和减法优先级为1,乘法和除法优先级为2)。
  2. 遍历到"(",将其入栈。
  3. 遍历到3,将其转换为整数并入栈。
  4. 遍历到"+",栈顶是"(",将"+"入栈。
  5. 遍历到4,将其转换为整数并入栈。
  6. 遍历到")",弹出栈顶运算符"+"并进行计算,得到3+4=7,并将计算结果7入栈。
  7. 遍历到"",栈顶元素是7,优先级大于"",将"*"入栈。
  8. 遍历到5,将其转换为整数并入栈。
  9. 遍历到"-",栈顶元素是"",优先级小于等于"-",弹出栈顶运算符""并进行计算,得到7*5=35,并将计算结果35入栈。
  10. 遍历到6,将其转换为整数并入栈。
  11. 遍历到"/",栈顶元素是6,优先级小于等于"/",弹出栈顶运算符"/"并进行计算,得到6/2=3,并将计算结果3入栈。
  12. 遍历完中缀表达式后,栈中仅剩下一个元素35-3,即为表达式的计算结果。

因此,中缀表达式(3 + 4) * 5 - 6 / 2的值为32。

中缀表达式如何转换为后缀表达式

将中缀表达式转换为后缀表达式的一种常用方法是使用栈。

以下是转换过程的步骤:

  1. 创建一个空栈和一个空列表,用于存储后缀表达式。
  2. 从左到右遍历中缀表达式中的每个元素。
  3. 如果当前元素是数字或字母,则直接添加到后缀表达式列表的末尾。
  4. 如果当前元素是左括号"(",则将其入栈。
  5. 如果当前元素是右括号")",则将栈中的元素弹出并添加到后缀表达式列表,直到遇到左括号"("。然后将左括号从栈中弹出,但不将其添加到后缀表达式列表中。
  6. 如果当前元素是运算符(如"+", "-", "*", "/"等),则进行以下操作:
  • 如果栈为空,则将当前运算符入栈。
  • 如果栈不为空,并且栈顶元素是左括号"(",则将当前运算符入栈。
  • 如果栈不为空,并且当前运算符的优先级小于等于栈顶运算符的优先级,则将栈顶运算符弹出并添加到后缀表达式列表,重复此步骤直到满足条件,然后将当前运算符入栈。
  • 如果栈不为空,并且当前运算符的优先级大于栈顶运算符的优先级,则将当前运算符入栈。
  1. 当遍历完中缀表达式后,将栈中剩余的运算符依次弹出并添加到后缀表达式列表。
  2. 后缀表达式列表即为转换后的后缀表达式。

以下是一个示例:

中缀表达式:2 + 3 * 4

转换过程:

  1. 遍历到2,直接添加到后缀表达式列表。
  2. 遍历到+,栈为空,将其入栈。
  3. 遍历到3,直接添加到后缀表达式列表。
  4. 遍历到*,栈不为空,栈顶是+,将*入栈。
  5. 遍历到4,直接添加到后缀表达式列表。
  6. 遍历完中缀表达式,将栈中剩余的运算符+和*依次弹出并添加到后缀表达式列表。

转换后的后缀表达式:2 3 4 * +

因此,中缀表达式2 + 3 * 4可以转换为后缀表达式2 3 4 * +。

[数据启示录 02] 堆栈_入栈_13

[数据启示录 02] 堆栈_堆栈_14

[数据启示录 02] 堆栈_运算符_15

[数据启示录 02] 堆栈

[数据启示录 02] 堆栈_堆栈_16

标签:02,入栈,元素,栈顶,运算符,启示录,堆栈,表达式
From: https://blog.51cto.com/u_16076194/8823443

相关文章

  • 2023-12-14 npm和yarn无法拉取依赖,cnpm可以 ==》切换镜像源
    这两天遇到个问题,是关于依赖无法拉取的问题,尽管我有三分猜到了是什么原因,但我还是不肯往那个方向思考,哎,真是死牛一便颈。如,我要给前端项目装个express框架,用npm装,装了大半天一点反应都没有,用yarn装就直接报网络无法连接,如图: 用cnpm装就没问题,秒过。注意:我的电脑是能正常上网......
  • 2023-2024-1 20231320 《计算机基础与程序设计》第十二周学习总结
    2023-2024-120231320《计算机基础与程序设计》第十二周学习总结作业信息这个作业属于哪个课程<班级的链接>(2023-2024-1计算机基础与程序设计)这个作业要求在哪里<作业要求的链接>(2022-2023-1计算机基础与程序设计第十二周作业)这个作业的目标<自学《C语言程序......
  • 2023-2024-1 20232301 《网络》第六周学习总结
    教材学习内容总结教材学习中的问题和解决过程问题1:对于习题中“如果针对差分你的一个统计查询是否可以无限制的进行重复查询?为什么?”这个问题经思考无果,存在困难问题1解决方案:询问chatgpt,得到了以下答案:在差分隐私(DifferentialPrivacy)的上下文中,无限制地进行重复查询是不可......
  • 2023年度总结
    又到了一年一度的总结时刻。对自己一年的工作做一些复盘和反思。从成败之中汲取经验教训,希望明年能更进一步。首先总结一下今年的一些工作,一月份至二月份主要完成了两件事,去年设计完成的芯片进行Signoff,以及投稿VLSI,中间插了个过年。时间紧任务重,大年夜家人在打牌,我在旁边赶论文......
  • 2023-2024-1学期20232423《网络空间安全导论》第六周学习总结
    教材学习——应用安全基础应用安全概述云计算造成了数据所有权和管理权的分离,在以下两方面开展持续研究:云计算基础设施的可信性、云数据安全保障。工业互联网:形成跨设备、跨系统、跨厂区、跨地区的互联互通,推动整个制造服务体系智能化。数据汇集到云端,要保证系统的可靠运行,......
  • 2023最新初级难度C#面试题,包含答案。刷题必备!记录一下。
    好记性不如烂笔头内容来自面试宝典-初级难度C#面试题合集问:请简单介绍一下C#是什么,以及它的主要特点有哪些?C#是由Microsoft公司开发的一款面向对象的编程语言,它运行于.NETFramework之上,可用于创建各种类型的应用程序,如桌面应用、移动应用、游戏和Web应用等。关于C#的主......
  • 2023最新中级难度C#面试题,包含答案。刷题必备!记录一下。
    好记性不如烂笔头内容来自面试宝典-中级难度C#面试题合集问:请描述C#中的垃圾回收如何工作?如何优化垃圾回收的性能?垃圾回收是一种自动内存管理技术,用于识别和释放不再使用的内存块。在C#中,垃圾回收器会定期扫描程序以查找不再使用的对象。一旦找到这些对象,就会标记它们以......
  • 2023 idea 常用插件
    AlibabaJavaCodingGuidelines阿里巴巴代码规范检查插件AiXcoderCodeCompleter代码提示补全插件ArthasIdeaArthas是阿里开源的Java在线诊断工具,该插件可以自动生成Arthas在线Java代码诊断命令AutofillingJavacallarguments代码生成插件。通过快捷......
  • 2023全球开发者生态调研:84%的开发者表示他们在工作中正积极使用生成式AI工具
    今年JetBrains首次在一年一度的开发者生态调研中,增加了人工智能方向的问题。在全球26348名开发者参与的调研中,总体对人工智能的发展持乐观态度。特别是生成式AI在软件开发和编程环节中的应用,84%的开发者表示他们在工作中正在积极使用生成式AI工具。调研中显示,AI文本生成工具比代......
  • 2023-2024-1 20232312 《网络空间安全导论》第六周学习
    2023-2024-120232312《网络空间安全导论》第六周学习教材学习内容总结6.1应用安全概述应用安全情况概述:在各类应用服务系统重,身份认证是保障应用安全的基础,其不仅仅包括传统的人的身份认证、软件等网络实体都需要身份认证和可信管理。不同场景、不同约束条件下都需要采用......