堆栈应用四则运算表达式求值

以最简单的方式实现几个C语言课程设计的常见题目,适合大一或者刚学习C语言的同学学习参考。使用Code::Blocks编译器创建的纯C项目,将其中的源码粘贴进其他编译器或C++项目也可直接运行。因为部分同学没有学习过数据结构,所以尽量使用传统的数组进行存储,规避没有学习过的知识点,但鼓励大家自己改进。为了使得程序更加简单方便阅读,基本上没有进行对用户输入的容错,可以自己添加。

Code::Blocks安装和使用 https://blog.csdn.net/qq_42283621/article/details/124055391?spm=1001.2014.3001.5501

项目源码会在文章末尾给出


参考文章:https://blog.csdn.net/qq_42446156/article/details/108162504
https://www.bilibili.com/video/BV1xp4y1r7rc?spm_id_from=333.337.search-card.all.click

中缀表达式 vs. 后缀表达式(逆波兰表达式)

中缀表达式:操作符在操作数的中间,就是我们常见表达式的类型,需要括号来帮助定义计算的顺序,如果把结果去掉就会产生歧义。
1 + ( ( 2 + 3 ) × 4 ) - 5:2+3=5;5×4=20;1+20=21;21-5=16
1 + 2 + 3 × 4 - 5:3 × 4 = 12;1+ 2 = 3;3 + 12 = 15;15 - 5 = 10

后缀表达式:操作符在操作数的后面,不需要括号,不会产生歧义。 1 2 3 + 4 × + 5 –
1 2 3 + 4 × + 5 –:2 3 + = 5;5 4 × = 20;1 20 + = 21;21 5 - =16

中缀表达式 转 后缀表达式的步骤:
初始化运算符栈s 和 结果队列q;
从左至右扫描中缀表达式:
遇到操作数时,将其加入q;
遇到运算符t时:
1.s空 / s栈顶为"(" / 运算符t的优先级大于栈顶元素的优先级,运算符t入栈s;× / 优先级大于 + -
2.否则,不断将s栈顶的运算符弹出并加入q中直至 s空 / s栈顶为"(" / 运算符t的优先级大于栈顶元素的优先级(t的优先级和栈顶元素优先级相同也是需要弹出的),然后运算符t入栈。
遇到括号时:
1.如果是左括号“(”,则直接压入s
2.如果是右括号“)”,则依次弹出s栈顶的运算符加入q直至遇到左括号"("为止,左右括号丢弃并不加入到q中

看一下上面的例子:
image-20220420142625421

分割字符串 和 类型转换

首先,你要求用户输入的表达式是一个字符串,你需要分割出哪些是数字,哪些是操作符。下面的代码仅供参考。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//输入字符是否是操作符 + - * / ( )
//是返回1,否返回0
int isOperator(char key)
{
    
    
    if (key == '+' || key == '-'
        || key == '*'|| key == '/'
        || key == '('|| key == ')')
        return 1;
    return 0;
}

int main()
{
    
    
    //原始输入
    char rawInput[50];
    printf("输入中缀表达式:");
    scanf("%s", rawInput);

    //字符串分割后的输入
    char processedInput[50][20];
    //分割后包含几个项
    int cnt = 0;

    int index = 0;
    for (int i = 0; i < strlen(rawInput); ++i)
    {
    
    
        //如果该字符是操作符
        if (isOperator(rawInput[i]))
        {
    
    
            //如果前一个字符不是操作符,那么在当前项的末尾加一个'\0'表示当前项数字结束,并将当前项设置为下一项
            if (i > 0 && isOperator(rawInput[i - 1]) == 0)
            {
    
    
                processedInput[cnt][index] = '\0';
                index = 0;
                ++cnt;
            }

            //存储操作符,然后将当前项设置为下一项
            processedInput[cnt][0] = rawInput[i];
            processedInput[cnt][1] = '\0';
            ++cnt;
        }
        //如果该字符不是操作符,那么把该字符添加到当前项的末尾,即当前项的数字还没有结束
        else
        {
    
    
            processedInput[cnt][index] = rawInput[i];
            ++index;
            if (i == strlen(rawInput) - 1)
            {
    
    
                processedInput[cnt][index] = '\0';
                ++cnt;
            }
        }
    }

    //输出测试
    printf("%d\n\n", cnt);
    for (int i = 0; i < cnt; ++i)
        printf("%s\n", processedInput[i]);

    return 0;
}

image-20220420165855756

字符串转数字

接下来实现,从char数组到double数字的变换

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

//字符串 转 小数
double string2Double(char* key)
{
    
    
    double result = 0;
    //小数点的位置,-1表示没有小数点
    int index = -1;
    for (int i = 0; i < strlen(key); ++i)
    {
    
    
        if (key[i] == '.')
        {
    
    
            index = i;
        }
        else
        {
    
    
            result = result * 10 + (key[i] - '0');
        }
    }
    if (index != -1)
        result /= pow(10, strlen(key) - index - 1);
    return result;
}

int main()
{
    
    
    char t[20];
    scanf("%s", t);
    printf("%lf", string2Double(t));

    return 0;
}

image-20220420153720691

栈 和 队列 的实现

栈 先进后出,队列 先进先出。我们本实验所使用的队列只有基本的 全部入队 全部出队,所以直接使用普通的数组代替即可。下面是栈的实现。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char stack[50][20];
//栈顶位置,初始时没有元素
int sTop = -1;
//入栈
void push(char *key)
{
    
    
    ++sTop;
    strcpy(stack[sTop], key);
}
//出栈
void pop()
{
    
    
    --sTop;
}
//查看栈顶元素,无元素返回NULL
char* top()
{
    
    
    if (sTop == -1)
        return NULL;
    else
        return stack[sTop];
}

char queue[50][20];
//队列中的元素个数
int qCnt = 0;

int main()
{
    
    
    int cmd;
    char input[20];
    while(1)
    {
    
    
        printf("\t\t0 退出\n\t\t1 入栈\n\t\t2 出栈\n\t\t3 查看栈顶元素\n\n");
        scanf("%d", &cmd);

        if (cmd == 0)
            break;
        switch(cmd)
        {
    
    
            case 1: printf("输入元素:"); scanf("%s", input); push(input); break;
            case 2: pop(); break;
            case 3: printf("栈顶元素:%s\n", top()); break;
        }
    }

    return 0;
}

image-20220420155915741

中缀表达式 转 后缀表达式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

char stack[50][20];
//栈顶位置,初始时没有元素
int sTop = -1;
//入栈
void push(char *key)
{
    
    
    ++sTop;
    strcpy(stack[sTop], key);
}
//出栈
void pop()
{
    
    
    --sTop;
}
//查看栈顶元素,无元素返回NULL
char* top()
{
    
    
    if (sTop == -1)
        return NULL;
    else
        return stack[sTop];
}

char queue[50][20];
//队列中的元素个数
int qCnt = 0;

//字符串 转 小数
double string2Double(char* key)
{
    
    
    double result = 0;
    //小数点的位置,-1表示没有小数点
    int index = -1;
    for (int i = 0; i < strlen(key); ++i)
    {
    
    
        if (key[i] == '.')
        {
    
    
            index = i;
        }
        else
        {
    
    
            result = result * 10 + (key[i] - '0');
        }
    }
    if (index != -1)
        result /= pow(10, strlen(key) - index - 1);
    return result;
}

//输入字符是否是操作符 + - * / ( )
//是返回1,否返回0
int isOperator(char key)
{
    
    
    if (key == '+' || key == '-'
        || key == '*'|| key == '/'
        || key == '('|| key == ')')
        return 1;
    return 0;
}

//运算符 a的优先级大于b的优先级返回1,否则返回0
int highPriority(char a, char b)
{
    
    
    if ((a == '*' || a == '/') && (b == '+' || b == '-'))
        return 1;
    return 0;
}

int main()
{
    
    
    //原始输入
    char rawInput[50];
    printf("输入中缀表达式:");
    scanf("%s", rawInput);

    //字符串分割后的输入
    char processedInput[50][20];
    //分割后包含几个项
    int cnt = 0;

    int index = 0;
    for (int i = 0; i < strlen(rawInput); ++i)
    {
    
    
        //如果该字符是操作符
        if (isOperator(rawInput[i]))
        {
    
    
            //如果前一个字符不是操作符,那么在当前项的末尾加一个'\0'表示当前项数字结束,并将当前项设置为下一项
            if (i > 0 && isOperator(rawInput[i - 1]) == 0)
            {
    
    
                processedInput[cnt][index] = '\0';
                index = 0;
                ++cnt;
            }

            //存储操作符,然后将当前项设置为下一项
            processedInput[cnt][0] = rawInput[i];
            processedInput[cnt][1] = '\0';
            ++cnt;
        }
        //如果该字符不是操作符,那么把该字符添加到当前项的末尾,即当前项的数字还没有结束
        else
        {
    
    
            processedInput[cnt][index] = rawInput[i];
            ++index;
            if (i == strlen(rawInput) - 1)
            {
    
    
                processedInput[cnt][index] = '\0';
                ++cnt;
            }
        }
    }

    //中缀 转 后缀
    for (int i = 0; i < cnt; ++i)
    {
    
    
        //操作数
        if (isOperator(processedInput[i][0]) == 0)
        {
    
    
            strcpy(queue[qCnt], processedInput[i]);
            ++qCnt;
        }
        // (
        else if (processedInput[i][0] == '(')
        {
    
    
            push(processedInput[i]);
        }
        // )
        else if (processedInput[i][0] == ')')
        {
    
    
            while (1)
            {
    
    
                if (top()[0] == '(')
                {
    
    
                    pop();
                    break;
                }
                else
                {
    
    
                    strcpy(queue[qCnt], top());
                    ++qCnt;
                    pop();
                }
            }
        }
        // + - * /
        else
        {
    
    
            //将s栈顶的运算符弹出并加入q中直至 s空 / s栈顶为"(" / 运算符t的优先级大于栈顶元素的优先级
            while(top() != NULL && top()[0] != '(' && highPriority(processedInput[i][0], top()[0]) == 0)
            {
    
    
                strcpy(queue[qCnt], top());
                ++qCnt;
                pop();
            }
            push(processedInput[i]);
        }
    }
    while (top() != NULL)
    {
    
    
        strcpy(queue[qCnt], top());
        ++qCnt;
        pop();
    }


    //测试输出后缀表达式
    for (int i = 0; i < qCnt; ++i)
        printf("%s ", queue[i]);
    printf("\n");

    return 0;
}

image-20220420162354946

后缀表达式计算

预备一个空栈s
从左至右扫描后缀表达式:
遇到操作数时,将其加入s;
遇到运算符t时:弹出栈顶的两个元素,进行运算,将结果入栈

附录——全部代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

char stack[50][20];
//栈顶位置,初始时没有元素
int sTop = -1;
//入栈
void push(char *key)
{
    
    
    ++sTop;
    strcpy(stack[sTop], key);
}
//出栈
void pop()
{
    
    
    --sTop;
}
//查看栈顶元素,无元素返回NULL
char* top()
{
    
    
    if (sTop == -1)
        return NULL;
    else
        return stack[sTop];
}

char queue[50][20];
//队列中的元素个数
int qCnt = 0;

//字符串 转 小数
double string2Double(char* key)
{
    
    
    double result = 0;
    //小数点的位置,-1表示没有小数点
    int index = -1;
    for (int i = 0; i < strlen(key); ++i)
    {
    
    
        if (key[i] == '.')
        {
    
    
            index = i;
        }
        else
        {
    
    
            result = result * 10 + (key[i] - '0');
        }
    }
    if (index != -1)
        result /= pow(10, strlen(key) - index - 1);
    return result;
}

//输入字符是否是操作符 + - * / ( )
//是返回1,否返回0
int isOperator(char key)
{
    
    
    if (key == '+' || key == '-'
        || key == '*'|| key == '/'
        || key == '('|| key == ')')
        return 1;
    return 0;
}

//运算符 a的优先级大于b的优先级返回1,否则返回0
int highPriority(char a, char b)
{
    
    
    if ((a == '*' || a == '/') && (b == '+' || b == '-'))
        return 1;
    return 0;
}

int main()
{
    
    
    //原始输入
    char rawInput[50];
    printf("输入中缀表达式:");
    scanf("%s", rawInput);

    //字符串分割后的输入
    char processedInput[50][20];
    //分割后包含几个项
    int cnt = 0;

    int index = 0;
    for (int i = 0; i < strlen(rawInput); ++i)
    {
    
    
        //如果该字符是操作符
        if (isOperator(rawInput[i]))
        {
    
    
            //如果前一个字符不是操作符,那么在当前项的末尾加一个'\0'表示当前项数字结束,并将当前项设置为下一项
            if (i > 0 && isOperator(rawInput[i - 1]) == 0)
            {
    
    
                processedInput[cnt][index] = '\0';
                index = 0;
                ++cnt;
            }

            //存储操作符,然后将当前项设置为下一项
            processedInput[cnt][0] = rawInput[i];
            processedInput[cnt][1] = '\0';
            ++cnt;
        }
        //如果该字符不是操作符,那么把该字符添加到当前项的末尾,即当前项的数字还没有结束
        else
        {
    
    
            processedInput[cnt][index] = rawInput[i];
            ++index;
            if (i == strlen(rawInput) - 1)
            {
    
    
                processedInput[cnt][index] = '\0';
                ++cnt;
            }
        }
    }

    //中缀 转 后缀
    for (int i = 0; i < cnt; ++i)
    {
    
    
        //操作数
        if (isOperator(processedInput[i][0]) == 0)
        {
    
    
            strcpy(queue[qCnt], processedInput[i]);
            ++qCnt;
        }
        // (
        else if (processedInput[i][0] == '(')
        {
    
    
            push(processedInput[i]);
        }
        // )
        else if (processedInput[i][0] == ')')
        {
    
    
            while (1)
            {
    
    
                if (top()[0] == '(')
                {
    
    
                    pop();
                    break;
                }
                else
                {
    
    
                    strcpy(queue[qCnt], top());
                    ++qCnt;
                    pop();
                }
            }
        }
        // + - * /
        else
        {
    
    
            //将s栈顶的运算符弹出并加入q中直至 s空 / s栈顶为"(" / 运算符t的优先级大于栈顶元素的优先级
            while(top() != NULL && top()[0] != '(' && highPriority(processedInput[i][0], top()[0]) == 0)
            {
    
    
                strcpy(queue[qCnt], top());
                ++qCnt;
                pop();
            }
            push(processedInput[i]);
        }
    }
    while (top() != NULL)
    {
    
    
        strcpy(queue[qCnt], top());
        ++qCnt;
        pop();
    }


    //测试输出后缀表达式
    printf("后缀表达式:");
    for (int i = 0; i < qCnt; ++i)
        printf("%s ", queue[i]);
    printf("\n");

    double doubleStack[50];
    int sDTop = -1;
    //后缀表达式计算
    for (int i = 0; i < qCnt; ++i)
    {
    
    
        if (isOperator(queue[i][0]))
        {
    
    
            double result = 0;
            double tb = doubleStack[sDTop];
            --sDTop;
            double ta = doubleStack[sDTop];
            --sDTop;
            switch(queue[i][0])
            {
    
    
                case '+': result = ta + tb; break;
                case '-': result = ta - tb; break;
                case '*': result = ta * tb; break;
                case '/': result = ta / tb; break;
            }
            ++sDTop;
            doubleStack[sDTop] = result;
        }
        else
        {
    
    
            ++sDTop;
            doubleStack[sDTop] = string2Double(queue[i]);
        }
    }

    printf("结果:%lf\n", doubleStack[0]);


    return 0;
}

image-20220420165558208

image-20220420165624249

猜你喜欢

转载自blog.csdn.net/qq_42283621/article/details/124302034
今日推荐