数据结构 | 栈的中缀表达式求值

简介: 数据结构 | 栈的中缀表达式求值

什么是栈?


栈是一种线性数据结构,具有“先进后出”(Last In First Out, LIFO)的特点。它可以看作是一种受限的线性表,只能在表的一端进行插入和删除操作,这一端被称为栈顶,另一端被称为栈底。不含任何元素的栈称为空栈。

d6194e52166b46a9b983ed220511cbdd.png




   栈的基本操作包括:入栈、出栈、取栈顶元素等。

栈的基本操作

  1. 理解栈的基本原理和操作;
  2. 掌握栈在表达式求值中的应用。

入栈操作



8b5eaffaf69e43bcb0b1c366ae613521.gif

出栈操作


24c06a0fb8424d72abf42af762885550.gif


取栈顶元素


71b0bd13a94040dca6c1a32409121020.gif


中缀表达式求值

中缀表达式是最常见的表达式表示方式,其表示形式为“操作数1 操作符 操作数2”。例如:


3+4


同样表示加法运算,参数分别为3和4,其结果为7。

对于表达式求值,我们通常使用中缀表达式,需要转换为前缀或后缀表达式。转换完成后,可以直接使用栈来求解表达式的值。


实现思路


中缀表达式求值比较复杂,需要考虑运算符的优先级以及括号的影响。因此,我们一般使用栈来实现算法。

具体实现过程如下:


  1. 初始化两个栈:运算符栈s1,存储中间结果的栈s2;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压入s2;
  4. 遇到运算符时,比较其与s1栈顶运算符的优先级:
  5. 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
  6. 否则,若优先级比栈顶运算符的优先级高,则将运算符压入s1;
  1. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到步骤4-1与s1中新的栈顶运算符相比较;
  1. 遇到括号时:
  1. 如果是左括号“(”,则直接压入s1;
  1. 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
  2. 重复步骤2-5,直到表达式的最右边;
  3. 将s1中剩余的运算符依次弹出并压入s2;
  1. 依次弹出s2中的元素计算结果。

我们将使用C语言来实现栈的中缀表达式求值功能。具体步骤如下:


  1. 定义栈结构体和相关操作函数(如初始化、入栈、出栈、取栈顶元素等);
  2. 定义字符类型的栈用于存储运算符,定义浮点数类型的栈用于存储操作数和中间结果;
  3. 实现后缀表达式求值函数,使用上述算法;
  4. 编写主函数,测试中缀表达式求值函数。


具体代码

#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
    int data;
    struct Node *next;
} Node;
typedef struct {
    Node *top;
} Stack;
int is_operator(char ch) {
    return (ch == '+' || ch == '-' || ch == '*' || ch == '/');
}
int priority(char op) {
    switch (op) {
        case '+':
        case '-':
            return 1;
        case '*':
        case '/':
            return 2;
        default:
            return 0;
    }
}
int calculate(int a, char op, int b) {
    switch (op) {
        case '+':
            return a + b;
        case '-':
            return a - b;
        case '*':
            return a * b;
        case '/':
            return a / b;
        default:
            exit(1);
    }
}
Stack *create_stack() {   // 创建空栈
    Stack *stack = (Stack *)malloc(sizeof(Stack));
    stack->top = NULL;
    return stack;
}
void push(Stack *stack, int data) {   // 入栈操作
    Node *node = (Node *)malloc(sizeof(Node));
    node->data = data;
    node->next = stack->top;
    stack->top = node;
}
int pop(Stack *stack) {   // 出栈操作
    if (stack->top == NULL) {
        return -1;   // 如果栈为空,则返回-1
    }
    Node *node = stack->top;
    int data = node->data;
    stack->top = node->next;
    free(node);
    return data;
}
int top(Stack *stack) {   // 返回栈顶元素
    if (stack->top == NULL) {
        return -1;   // 如果栈为空,则返回-1
    }
    return stack->top->data;
}
/**
 * 计算表达式结果的函数。
 *
 * @param expression 表达式字符串
 * @return 表达式的计算结果
 */
int evaluate_expression(char *expression) {
    Stack *s1 = create_stack();   // 操作数栈,用于存储操作数
    Stack *s2 = create_stack();   // 操作符栈,用于存储操作符
    for (int i = 0; expression[i] != '\0'; i++) {
        if (is_operator(expression[i])) {   // 如果当前字符为操作符
            while (s2->top != NULL && priority(top(s2)) >= priority(expression[i])) {
                // 如果操作符栈不为空,并且栈顶操作符的优先级大于等于当前操作符的优先级
                int b = pop(s1);    // 出栈一个操作数作为运算的右操作数
                int a = pop(s1);    // 再出栈一个操作数作为运算的左操作数
                char op = pop(s2);  // 出栈一个操作符
                int result = calculate(a, op, b);   // 计算两个操作数和操作符的结果
                push(s1, result);   // 将计算结果入栈到操作数栈中
            }
            push(s2, expression[i]);  // 当前操作符入栈到操作符栈中
        } else if (expression[i] == '(') { // 如果当前字符为左括号
            push(s2, expression[i]);    // 入栈到操作符栈中
        } else if (expression[i] == ')') { // 如果当前字符为右括号
            while (top(s2) != '(') {    // 循环执行直到遇到左括号
                int b = pop(s1);    // 出栈一个操作数作为运算的右操作数
                int a = pop(s1);    // 再出栈一个操作数作为运算的左操作数
                char op = pop(s2);  // 出栈一个操作符
                int result = calculate(a, op, b);   // 计算两个操作数和操作符的结果
                push(s1, result);   // 将计算结果入栈到操作数栈中
            }
            pop(s2);    // 弹出左括号
        } else {    // 如果当前字符为数字
            int num = expression[i] - '0';  // 将当前字符转换成数字
            while (expression[i + 1] >= '0' && expression[i + 1] <= '9') {
                // 如果下一个字符也是数字,则将其合并到一起
                num = num * 10 + expression[++i] - '0';
            }
            push(s1, num);  // 将数字入栈到操作数栈中
        }
    }
    // 处理剩余的操作符
    while (s2->top != NULL) {
        int b = pop(s1);    // 出栈一个操作数作为运算的右操作数
        int a = pop(s1);    // 再出栈一个操作数作为运算的左操作数
        char op = pop(s2);  // 出栈一个操作符
        int result = calculate(a, op, b);   // 计算两个操作数和操作符的结果
        push(s1, result);   // 将计算结果入栈到操作数栈中
    }
    return pop(s1); // 返回最终的计算结果
}
int main() {
    char expression[100];
    printf("请输入中缀表达式:");
    scanf("%s", expression);
    int result = evaluate_expression(expression);
    printf("计算结果:%d\n", result);
    return 0;
}


相关文章
|
2月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
45 1
|
17天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
132 77
|
18天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
38 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
18天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
40 9
|
18天前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
32 7
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
91 5
|
2月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
104 21
|
2月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
2月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
65 4
|
2月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
62 0

热门文章

最新文章