中缀表达式转后缀表达式

基本概念就不多介绍了,直接开始介绍思路以及容易踩的坑。

思路要点

1. 整个过程建立在栈的基础上实现。

  • 需要准备的栈相关的函数:
    Push: 压栈
    Pop: 弹栈
    Top: 查看栈顶元素
    isEmpty: 判断栈是否为空

  • 建议用数组结构实现栈的结构体声名,书上说这样更符合未来实际开发。
    关于实现方式的细节可以查看: 三种栈的实现方式

2. 我们需要准备一个栈和两个字符型的数组

  • 准备的栈的目的是改变原中缀表达式中符号的出场顺序,以一种适合计算机运算的出场顺序(后缀表达式)让计算机得到结果。这个栈内只能进出运算符号,跟数字没关系。
  • 第一个字符组用来记录用户输入的中缀表达式 "input"
    第二个字符组用来记录用户输出的后缀表达式 "Postfix"

3. 清晰划分各种符号的等级

等级用于决定一个新的符号即将入栈时是否将符号栈中的原有的符号弹出

  • level 2 <kbd>*</kbd> 和 <kbd>/</kbd>
  • level 1 <kbd>+</kbd> 和 <kbd>-</kbd>
  • level 0 <kbd>(</kbd> 和 <kbd>)</kbd>

4. 中缀表达式的读取

  • 读到数字直接写入后缀表达式 Postfix 的字符组
  • 读到字符(运算符)将字符交给处理字符比较的函数 “OperatorCmp”

5. OperatorCmp 字符比较

  • 传入两个参数 now 和 check
    now 是在读取中缀表达式时识别到的字符,也是即将入栈的字符。
    check 是通过Top函数返回的栈顶的元素。
    这个函数目的在于通过now的级别和运算符种类,决定下一步栈中元素的动态。
  • 栈中元素的改变(按照优先级顺序叙述,用else if)
    • 第一种情况:栈中没有元素,第一个运算符直接入栈(这种情况要在函数外进行特殊情况处理,因为函数需要传入栈顶元素值,栈空时强制传入值可能造成数组越界)
    • 第二种情况:now是<kbd>(</kbd>,直接将now压栈
    • 第三种情况:now是<kbd>)</kbd>,在阐述完后两种情况后再回来讲述。
    • 第四种情况:now.level <= check.level
      意味着这是<kbd></kbd>或<kbd>/</kbd>,这里我们要和栈顶元素进行比较。
      如果 now.level <= check.level 我们要将栈顶元素弹栈,将弹栈元素加入 Postfix 字符组中。
      这还没完!我们的比较还没有结束,根据后缀表达式的定义,我们很有可能掉进只将前面一个元素弹出的
      *,我们的比较只有两个等级而且最后我们还会执行统一清栈的操作,如果这个位置不连续对栈顶元素进行比较,会将本该在这部分弹栈的元素在最后清栈的操作中弹出,由于我们只有两个等级,所以对最终计算结果没有影响,但如果等级变多(例如出现了^)或者某个OJ要求你输出后缀表达式,那就GG了。(测试:2 + 3 * 4 - 5 结果: 2 3 4 * + 5 -)
      所以,我们要循环多次比较栈顶元素,直到 now.level > check.level 循环跳出。
      最后不要忘记,将now压栈!结束。
    • 第五种情况:now.level > check.level
      将now压栈,结束。
    • 回过头来我们介绍第三种情况。识别到<kbd>)</kbd>时,我们应该做的操作是循环弹栈,直到识别到栈顶元素是<kbd>(</kbd>,那么问题来了,我们需不需像第四种和第五种情况一样考虑栈顶元素的等级决定是否弹栈呢?答案是不用,后括号前的元素已经帮你解决了这个难题,不然我们又要开启递归了。每个压栈元素触发的比较已经可以保证逆序弹栈此时栈中剩余元素,将弹栈元素加入 Postfix 字符组中就可以正常生成后缀表达式。
      所以我们要做的操作就是识别到<kbd>(</kbd>之前,循环弹栈。
      循环结束后完成。
示例

6. 清空字符栈

  • 将中缀表达式读取完后,后缀表达式 Postfix 还没有生成完毕,因为栈中会有剩余运算符元素,此处的处理方法等同于上面的第三种情况,可以想象将用户输入的中缀表达式两侧加上括号,自然解决办法相同。
    逆序弹栈此时栈中剩余元素,将弹栈元素加入 Postfix 字符组中。
    至此完成全部操作。

代码

这里是一个要求根据中缀表达式生成后缀表达式并计算出结果的结果代码。以供参考。有一些细节写的繁琐的是因为适应我们校奇怪的oj系统...

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

typedef struct Operator
{
    char moperator;
    int level;
}Operator;
Operator op[10];

typedef struct LinkStack
{
    Operator opNodes[100000];
    int top;
}LinkStack;
LinkStack opstack;

char Postfix[100000];
int PostfixNum = 0;

void InitOperater(){
    op[0].moperator = '+';
    op[0].level = 1;
    op[1].moperator = '-';
    op[1].level = 1;
    op[2].moperator = '*';
    op[2].level = 2;
    op[3].moperator = '/';
    op[3].level = 2;
    op[4].moperator = '(';
    op[4].level = 0;
    op[5].moperator = ')';
    op[5].level = 0;
}

int isEmpty(LinkStack &stack)
{
    return (stack.top == -1);
}

void pop(LinkStack &stack)
{
    stack.top--;
}

void push(LinkStack &stack, Operator pushed)
{
    stack.top++;
    stack.opNodes[stack.top] = pushed;
}

Operator Top(LinkStack &stack)
{
    return stack.opNodes[stack.top];
}

void operatorCmp(LinkStack &opstack,Operator now,Operator check){
    if(now.moperator == '('){
        push(opstack,now);
    }
    else if(now.moperator == ')'){
        now = Top(opstack);
        pop(opstack);
        check = Top(opstack);
        while(check.moperator != '('){
            Postfix[PostfixNum] = now.moperator;
            PostfixNum++;
            pop(opstack);
            now = check;
            check = Top(opstack);
        }
        Postfix[PostfixNum] = now.moperator;
        PostfixNum++;
        pop(opstack);
    }
    else if(check.level >= now.level){
           while(check.level >= now.level){
        Postfix[PostfixNum] = check.moperator;
        PostfixNum++;
        pop(opstack);
        if(isEmpty(opstack))break;
        check = Top(opstack);
       }
        push(opstack,now);
    }
    else if(check.level < now.level)
        push(opstack,now);
}
double FindSolution(char* Postfix){
    double Digit[100000];
    int DigitNum = 0;
    for(int i=0;i<PostfixNum;i++){
        if(Postfix[i]>='0'&&Postfix[i]<='9'){
            Digit[DigitNum] = Postfix[i]-'0';
            DigitNum++;
        }
        if(Postfix[i]=='+'){
            int firstNum = DigitNum - 1;
            int secondNum = DigitNum - 2;
            double first = Digit[firstNum];
            double second = Digit[secondNum];
            Digit[secondNum]=second+first;
            DigitNum--;
        }
        if(Postfix[i]=='-'){
            int firstNum = DigitNum - 1;
            int secondNum = DigitNum - 2;
            double first = Digit[firstNum];
            double second = Digit[secondNum];
            Digit[secondNum]=second-first;
            DigitNum--;
        }
        if(Postfix[i]=='*'){
            int firstNum = DigitNum - 1;
            int secondNum = DigitNum - 2;
            double first = Digit[firstNum];
            double second = Digit[secondNum];
            Digit[secondNum]=second*first;
            DigitNum--;
        }
        if(Postfix[i]=='/'){
            int firstNum = DigitNum - 1;
            int secondNum = DigitNum - 2;
            double first = Digit[firstNum];
            double second = Digit[secondNum];
            Digit[secondNum]=second/first;
            DigitNum--;
        }
    }
    return Digit[0];
}
int main(){
    InitOperater();
    opstack.top = -1;
    char s[100000];
    scanf("%s",s);
    int i=0;
    while(s[i]!='\0'){
        if (s[i] >= '0'&&s[i] <= '9') {
            Postfix[PostfixNum] = s[i];
            PostfixNum++;
        }
        else{
            for(int j = 0; j<= 5; j++){
                if (s[i] == op[j].moperator){
                    if(!isEmpty(opstack))operatorCmp(opstack,op[j],Top(opstack));
                    else push(opstack,op[j]);
                }
            }
        }
        i++;
    }
    while(!isEmpty(opstack)){
        Postfix[PostfixNum] = Top(opstack).moperator;
        PostfixNum++;
        pop(opstack);
    }
    double solution = FindSolution(Postfix);
    printf("%.2lf\n",solution);
    for(int i=0;i<PostfixNum;i++){
        printf("%c ",Postfix[i]);
    }
    return 0;
}



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容