2023-11-30 通过中缀表达式转换后缀表达式, 用C语言完成一个简单的计算器


点击 快速C语言入门


通过中缀表达式转换后缀表达式, 用C语言完成一个简单的计算器

  • 前言
  • 一、中缀表达式和后缀表达式 (AI辅助)
  • 二、中缀转后缀规则及后缀运算规则 (AI辅助)
  • 总结


前言

要解决问题: 在练习用Qt完成一个简单的计算器时, 需要将一个文本计算式转换为C语言可使用的模式, 即后缀表达式, 规则还是挺繁复的.

想到的思路: 查文档, 了解中缀表达式转换为后缀表达式.

其它的补充: 需要用到栈, 这个基本功一定要扎实.


一、中缀表达式和后缀表达式 (AI辅助)

中缀表达式是指常见的数学表达式,如 2 + 3 * 4 - 5,其中运算符位于操作数之间。

中缀表达式通常需要通过加入括号来明确运算顺序。

后缀表达式(也称为逆波兰表达式)是一种不需要括号的表达式表示方式,其中运算符位于其相应的操作数之后。

例如,上述中缀表达式的后缀形式为 2 3 4 * + 5 -

在计算机科学中,后缀表达式比中缀表达式更容易被计算机算法处理和计算,因为它们没有嵌套括号的优先级问题。

因此,在编写计算机程序解析表达式时,通常会将中缀表达式转换为等效的后缀表达式以便更轻松地处理它们。

二、中缀转后缀规则及后缀运算规则 (AI辅助)

在后缀表达式中,运算符总是在它们所操作的两个操作数之后出现。

例如,对于后缀表达式 “3 4 + 5 *”,运算规则如下:

  1. 先将操作数 3 和 4 压入栈中。

  2. 遇到 “+” 运算符,弹出栈顶的 3 和 4 进行加法运算,结果为 7,再将结果 7 压入栈中。

  3. 遇到操作数 5,将其压入栈中。

  4. 遇到 “*” 运算符,弹出栈顶的 7 和 5 进行乘法运算,结果为 35,再将结果 35 压入栈中。

  5. 后缀表达式中的所有元素都被处理后,栈中只剩下一个元素 35,即为后缀表达式的计算结果。

具体规则见代码注释:

#include 
#include 
#include 
#include 

// 节点
struct node
{
    int flag;
    int value;
    struct node *next;
};

// 栈
typedef struct node *Stack;

// 压栈
void push(Stack *stack, int flag, int value)
{
    struct node *head = malloc(sizeof(struct node));
    head->flag = flag;
    head->value = value;
    head->next = *stack;
    *stack = head;
}

// 出栈
int pop(Stack *stack)
{
    int rest = (*stack)->value;
    Stack temp = *stack;
    *stack = (*stack)->next;
    free(temp);
    return rest;
}

// 判断栈是否为空
int empty(Stack *stack)
{
    return (*stack) == NULL;
}

// 栈顶元素
int top(Stack *stack)
{
    int rest = (*stack)->value;
    return rest;
}

// 栈顶flag
int topFlag(Stack *stack)
{
    return (*stack)->flag;
}

// 释放栈
void freeStack(Stack *stack)
{
    while (*stack)
    {
        struct node *head = *stack;
        *stack = (*stack)->next;
        free(head);
    }
}

// 反转栈
void reverse(Stack *stack)
{
    if (!(*stack))
    {
        return;
    }

    Stack head;
    Stack tail = NULL;

    while (*stack)
    {
        head = *stack;
        *stack = (*stack)->next;
        head->next = tail;
        tail = head;
    }
    *stack = head;
}

// 返回优先级
int priority(char operate)
{
    switch (operate)
    {
    case '+':
    case '-':
        return 1;
    case '*':
    case '/':
    case '%':
        return 2;
    default:
        return 0;
    }
}

// 判断操作符
int isOperator(char operator)
{
    return (operator== '+' || operator== '-' || operator== '*' || operator==
            '/' ||
            operator== '%');
}

// 判断左括号
int isLeftParenthesis(char operator)
{
    return operator== '(';
}

// 判断右括号
int isRightParenthesis(char operator)
{
    return operator== ')';
}

// 中缀表达式转后缀表达式的规则如下:
void convertToPostfix(char *input, Stack *postfix)
{
    Stack operateStack = NULL;
    static char number[32];

    // 1.从左到右遍历中缀表达式的每个元素。
    for (int i = 0; input[i] != '\0'; i++)
    {
        // 2.如果当前元素是数字,则将其加入后缀表达式中。
        if (isdigit(input[i]))
        {
            int num = 0;
            while (isdigit(input[i]))
            {
                number[num++] = input[i++];
            }
            number[num] = '\0';
            i--;
            push(postfix, 1, atoi(number));
        }
        // 3.如果当前元素是操作符,则执行以下步骤:
        //   a.如果该操作符是左括号“(”,则将其加入操作符栈中。
        else if (isLeftParenthesis(input[i]))
        {
            push(&operateStack, 0, input[i]);
        }
        //   b.如果该操作符是右括号“)”,则将操作符栈中的操作符弹出并加入后缀表达式中,直到遇到左括号为止。
        else if (isRightParenthesis(input[i]))
        {
            while (!isLeftParenthesis((char)top(&operateStack)))
            {
                push(postfix, 0, pop(&operateStack));
            }
            pop(&operateStack);
        }
        else if (isOperator(input[i]))
        {
            //   c.如果该操作符的优先级比操作符栈顶操作符的优先级低或相等
            //     则将操作符栈中的操作符弹出并加入后缀表达式中
            //     直到不存在比当前操作符优先级高的操作符为止。
            while (!empty(&operateStack) &&
                   priority(input[i]) <= priority((char)top(&operateStack)))
            {
                push(postfix, 0, pop(&operateStack));
            }
            //     将当前操作符加入操作符栈中。
            //   d.如果该操作符的优先级比操作符栈顶操作符的优先级高,则将当前操作符加入操作符栈中。
            push(&operateStack, 0, input[i]);
        }
    }
    // 4.当中缀表达式遍历完后,将操作符栈中的所有操作符依次弹出并加入后缀表达式中。
    while (!empty(&operateStack))
    {
        push(postfix, 0, pop(&operateStack));
    }
    freeStack(&operateStack);
    reverse(postfix);
}

// 计算
int calculate(int operand1, int operand2, char operate)
{
    switch (operate)
    {
    case '+':
        return operand1 + operand2;
    case '-':
        return operand1 - operand2;
    case '*':
        return operand1 * operand2;
    case '/':
        return operand1 / operand2;
    case '%':
        return operand1 % operand2;
    default:
        return 0;
    }
}

// 计算后缀表达式的规则如下:
int evaluatePostfix(Stack *postfix)
{
    Stack stack = NULL;

    // 1.从左到右扫描后缀表达式。
    while (*postfix)
    {
        // 2.遇到操作数时,将其压入操作数栈中。
        if ((*postfix)->flag)
        {
            push(&stack, 1, pop(postfix));
        }
        // 3.遇到运算符时,弹出栈顶的两个操作数,执行该运算符的计算,并将结果压入操作数栈中。
        else if (!(*postfix)->flag)
        {
            int operand2 = pop(&stack);
            int operand1 = pop(&stack);
            int result = calculate(operand1, operand2, (char)pop(postfix));
            push(&stack, 1, result);
        }
    }
    // 4.重复上述过程,直到后缀表达式中的所有元素都被处理。
    // 5.最后,操作数栈中只剩下一个数,即为后缀表达式的计算结果。
    int result = pop(&stack);
    freeStack(&stack);
    return result;
}

// 打印后缀表达式栈
void printStack(Stack stack)
{
    while (stack)
    {
        if (stack->flag)
        {
            printf("%d ", stack->value);
            stack = stack->next;
        }
        else
        {
            printf("%c ", stack->value);
            stack = stack->next;
        }
    }
    printf("\n");
}

// 测试
int main()
{
    char input[128];
    Stack postfix = NULL;

    printf("请输入含加减乘除、求模及括号的复杂算式:");
    scanf("%s", input);

    convertToPostfix(input, &postfix);
    printf("后缀表达式为:\n");
    printStack(postfix);

    int result = evaluatePostfix(&postfix);
    printf("计算结果为:%d\n", result);

    freeStack(&postfix);

    return 0;
}

总结

用C语言对字符串算式做语法分析并得出计算结果, 是很多教材的标准示例, 但并不简单, 属于数据结构和算法的一个类型, 考验栈结构和操作函数, 中缀转后缀算法, 后缀计算算法等, 慢慢体会.


点击 快速C语言入门


你可能感兴趣的:(笔记,c语言,开发语言,后缀表达式,算法)