经过栈实现中缀表达式到后缀表达式的转换

在编程的世界中数据结构和算法老是如影随行, 难舍难分的.git

栈做为一种常见的数据结构(抽象数据类型)在程序的世界中有很是的意义.算法

在计算机科学中,栈是一种抽象数据类型(ADT / Abstract Data Type),用做数据的集合表示.栈有两个主要的操做编程

  • push用于将元素推入栈中数据结构

  • pop 用于将元素从栈顶部弹出数据结构和算法

简单来讲,栈就是一个后入先出(LIFO / Last In First Out)的队列.现实生活中叠盘子就是一个形象的栈,新盘子只能在顶部堆叠进去,而抽盘子是从顶部一个个抽走.函数

栈的应用

栈在计算机中有很是普遍的应用,好比说函数的调用堆栈.谈点更实在的应用的话,栈能够很是方便的用来作平衡符号, 表达式求值和语法解析.
今天的重点是经过栈实现一个中缀表达式到后缀表达式的转换,为以后的构建表达式树作铺垫.指针

使用栈来实现平衡符号其实很是简单code

  • 遇到 '(', '[', '{'就将符号推入栈中递归

  • 遇到 ')', ']', '}'就弹出栈中的一个元素,查看是否匹配队列

  • 处理完全部数据以后栈应该为空.

这个算法的时间复杂度为O(N)而且这个算法是在线的.

后缀表达式

常见的表达式如a + b * c + g / f在计算这个表达式时,咱们必须明确记住操做符的优先级, +, -的优先级小于*, /因此表达式处理上就会比较复杂.若是咱们换种思路,将中缀表达式转换为后缀表达式,那处理就会简单不少.

这种记法其实就是将咱们口头上的说法搬到纸上.

  • b和c相乘 => b c *(A1)

  • a与b和c相乘的结果相加 => a (A1) +(A2)

  • g被f除 => g f /(A3)

  • 前面的结果与后面的结果相加 => A2 A3 +

  • A2 A3 +展开以后就获得了a b c * + g f / +

最后展开的结果就是中缀表达式a + b * c + g / f转换成后缀表达式的结果a b c * + g f / +,这种记法叫作后缀记法或者逆波兰记法.
计算这个表达式的值的简单方法就是使用栈.

  • 当遇到一个操做数时就将这个操做数推入栈中

  • 当遇到一个操做符时就从栈中弹出两个操做数, 并将这个操做符运用于弹出的两个操做数上

这种方法显而易见的一个大优势就是没有必要知道任何优先级规则.并且这个算法的时间复杂度是O(N), 并且是在线的.

那么问题来了,如何将中缀表达式转换成后缀表达式呢?

中缀 => 后缀

显然,咱们也能够经过栈来进行转换.记住如下几个规则

  • 当遇到一个操做数时,将这个操做数输出

  • 当遇到一个操做符时,将栈中大于等于该符号优先级的操做符弹出(并输出).而后将该操做符压入栈中

  • 当遇到特殊操做符开括号('(', '[', '{')时,将该符号压入栈中.可是把从这个符号开始当作一个新栈(能够考虑递归的调用栈)

  • 当遇到特殊操做符闭括号(')', ']', '}')时,将最新的那个栈中的元素所有弹出(并输出, 可是不输出弹出的开括号)

规则就这么多,是否是感受很难理解?咱们来看个栗子~

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h> // 用于判断字符类型

int sp = -1; // 栈顶指针, 始终指向栈顶, 空栈时指向栈底(0)之下
char symbol_stack[64] = { 0 }; // 显然,咱们须要个符号栈来存放操做符

// 定义几个栈操做
void push(char op); // 将符号 op 推入栈中
char pop(void); // 弹出栈顶元素
char top(void); // 查看栈顶的元素

int main(void) {
    // 定义一个输入表达式
    char *input = "a + b * c - g / f";
    
    // 开始处理输入表达式
    // 咱们只进行四则运算的转换
    for (int index = 0; index < strlen(input); ++index) {
        char current_character = input[index];
        char stack_top_character = top();

        // 若是是操做数,那就直接输出
        if (isdigit(current_character) || isalpha(current_character)) {
            printf("%c ", current_character);
            continue;
        }

        // 若是不是操做数,而且不是 '+', '-', '*', '/', '(', ')', ' ' 那就报错
        if (current_character != '+' && current_character != '-' &&
            current_character != '*' && current_character != '/' &&
            current_character != '(' && current_character != ')' &&
            current_character != ' ') {
            fprintf(stderr, "Unknown symbol `%c`\n", current_character);
            exit(1);
        }

        // 跳过空格和结束标志
        if (current_character == ' ' || current_character == '\0') {
            continue;
        }

        switch (current_character) {
            // 特殊操做符 (
            case '(':
                push(current_character);
                break;
            // 特殊操做符 )
            case ')':
                // 弹出新栈的全部元素, 或者直到为空栈
                while (stack_top_character != '(' && stack_top_character != '\0') {
                    printf("%c ", stack_top_character);
                    stack_top_character = pop();
                }
                pop(); // 弹出多余的 (
                break;
            case '*':
            case '/':
                // 除了 '(' ')' 以外, * / 具备最高的优先级, 除非栈中不可能有比 * / 更大的优先级
                // 因此只能弹出优先级相等的操做符(自己)
                while (stack_top_character == '*' || stack_top_character == '/') {
                    printf("%c ", pop());
                    stack_top_character = top();
                }
                // 执行完以后压入当前的符号
                push(current_character);
                break;
            case '+':
            case '-':
                // + - 具备最低的优先级, 因此弹出全部的操做符,除非是表明新栈的 (
                while (stack_top_character != '(' && stack_top_character != '\0') {
                    printf("%c ", pop());
                    stack_top_character = top();
                }
                // 执行完以后压入当前的符号
                push(current_character);
                break;
        }
    }
    // 最后执行清空全部栈
    while (top() != '\0') {
        printf("%c ", pop());
    }

    return 0;
}

// 入栈操做定义
void push(char op) {
    symbol_stack[++sp] = op;
}
// 出栈操做定义
char pop(void) {
    return symbol_stack[sp--];
}
// 查看操做定义
char top(void) {
    return sp == -1 ? '\0' : symbol_stack[sp];
}

看我代码是否是感受就一目了然了呢?若是仍是不懂的话,那是个人锅-^-(面壁思过).其实本身再纸上演算一下就好啦~接下来就是使用转换出的后缀表达式构建表达式树了,其实也是用栈的方式啦~

相关文章
相关标签/搜索