资讯专栏INFORMATION COLUMN

数据结构练习题之栈与队列:算术表达式转换成后缀表达式(C语言实现)

baiy / 1877人阅读

摘要:算术表达式转换成后缀表达式是一个经典的栈的应用。除此之外操作数之间的相对次序是不变的,并且后缀表达式中不含括号。输入一个算术表达式,以字符作为结束标志。输出该表达式转换所得到的后缀式。

目录

一、引言

二、问题分析

三、例题应用——一般算术表达式转换成后缀式

(1)栈的基本操作函数的定义

(2)主函数

(3)运行结果

 (四)、完整代码

(五)、总结


一、引言:

       花了一天的时间,用代码实现了这个功能,过程很痛苦,出现了很多错误,可能我比较菜,调试了一天才调试出来。俗话说:出现错误才是提升自我的开始。如果没有出现错误,那就永远发现不了问题,只有通过出现问题,分析问题,解决问题,才能有所收获,有所提升,过程是艰难的,但解决了问题的那一刻,像是中了大奖一样。算术表达式转换成后缀表达式是一个经典的栈的应用。思想是一样的,掌握了这个思想,所有的算术表达式转换成后缀表达式都能运用这个思想,代码通用。

转载请注明本文链接!

二、问题分析:

       什么是算术表达式:就是我们平时做的数学题的表达式,比如:a*b+(c-d/e)*f

       什么是后缀表达式:把操作符放在操作数的后面,上式对应的后缀表达式为:ab*cde/-f*+,越在前面的运算符越先执行。除此之外操作数之间的相对次序是不变的,并且后缀表达式中不含括号。

        我们要分析一下,如何设置条件,以至于让计算机能够明白我们的思想,明白我们想要计算机干什么,有些东西我们一眼就能,看出来,但是计算机却需要我们给它传达指令它才能去按照我们的想法去工作。

        对于栈来说,我们要首先知道栈是一个先进后出的数组,并且只允许在栈顶进行出栈和进栈操作。数组肯定有大小,我们可以先定义一个足够大的数组来存储我们输入的算术表达式。然后定义一个栈结构体,一个栈S1用来存储最终的后缀表达式,栈S1存储的位置是后缀表达式的相反顺序,我们可以用一个数组来获取这个栈S1的元素,输出的时候,从最后一位往前输出即可,为什么用栈S1来存储后缀表达式而不用数组呢,因为栈操作起来非常方便,执行时只需调用出栈进栈等函数就可以,当然你也可以用数组直接存储。另一个栈S2用来存储算术表达式中的操作符,先出栈的操作符先执行运算!所以我们就需要设置一些条件来判断什么时间S2中的操作符出栈,什么时间,让当前扫描到的操作符进栈。 

        扫描当前算术表达式,获取当前字符C:有以下几个出栈进栈的规则:

(1)、如果c=‘(’左括号,直接把它存入到后缀表达式的栈S2中。

        //如果是左括号,直接进栈        if(c=="(")        {            Push(&S2,c);        }

(2)、如果c=‘)’右括号,我们获取当前存储操作符的栈S2的栈顶元素,如果它不等于左括号‘(’,将栈S2的栈顶元素先进栈到S1中,然后S2执行出栈操作,接着再次获取栈S2的栈顶元素,继续执行(2)这个步骤,循环执行。直到第一次遇到‘(‘左括号,就停止循环,将’(‘从S2中出栈即可,继续扫描算术表达式的下一个字符。

        else if(c==")")        {            char b=Get(S2);//获取存操作符的栈顶元素            while(b!="(")//遇见左括号之前一直循环出栈S2            {                Push(&S1,b);//存入到栈S1                Pop(&S2);    //进行出栈S2                b=Get(S2);//再获取栈顶S2的元素                //printf("--------++%c/n",b);            }            Pop(&S2);

(3)、如果c!="+"并且c!="-"并且c!="*"并且c!="/"并且c!=")"。那我们就把它直接存入到后缀表达式的栈S1中。这里可以理解为c为数字或者是c为未知的一系列变量x。继续扫描算术表达式的下一个字符。

        else if(c!="+"&&c!="-"&&c!="*"&&c!="/"&&c!=")")//根据题目变换        {            Push(&S1,c);        }

(4)、如果c=="+"或者c=="-",也就是我们现在遇到了加减这两个操作符,我们先判断一下栈S2是否为空栈,如果为空栈的话,那我们直接将当前的操作符c进入栈S2中即可。如果不为空的话,我们获取一下栈S2的栈顶元素是什么。如果是左括号’(‘,那我们也直接将当前的操作符c进入栈S2中即可;如果不是左括号’(‘,那我们就将栈S2的栈顶元素先进栈到S1中,然后让S2执行出栈操作,接着再次获取栈S2的栈顶元素,循环执行(4)这个步骤,直到第一次遇到‘(‘左括号,或者是栈为空就停止循环,将’(‘从S2中出栈即可,继续扫描算术表达式的下一个字符,如果没有遇到左括号,那我们就一直出栈,直到栈空,然后把c压入到栈S2中。继续扫描算术表达式的下一个字符。

        else if(c=="+"||c=="-")        {            if(S2.top==-1)         //如果操作符栈为空,直接进栈            {                Push(&S2,c);            }            else            {                b=Get(S2);                while(b!="(")         //只要不为左括号或者是栈不为空,那就一直出栈                {                    Push(&S1,b);         //运算符存入到栈S1中                    Pop(&S2);                    if(S2.top!=-1)            //栈不为空,执行一次出栈                    {                        b=Get(S2);           //再获取栈顶                    }                    else                    {                        break;                    }                }                Push(&S2,c);            }        }

(5)、如果c=="*"||c=="/",也就是我们遇到了乘除这两个操作符,我们先判断一下栈S2是否为空栈,如果为空栈的话,我们直接将当前的操作符c进入到栈S2中即可。如果不为空的话,我们获取一下栈S2的栈顶元素,如果b!="("并且b!="+"并且b!="-",这里是用的并且,不是用的或者,我这里就出现了问题。只有当它不为这三个时,同时成立。我们才将当前S2的栈顶元素压入到栈S1中,然后栈S2执行出栈操作,然后再次获取栈S2的栈顶元素,再循环执行(5),直到当栈为空或者是栈顶元素等于上面的三个中的一个,我们才停止循环,将当前的操作符c进入栈S2中。继续扫描算术表达式的下一个字符。

    else if(c=="*"||c=="/")        {            if(S2.top==-1)            {               Push(&S2,c);            }            else            {                b=Get(S2);                while(b!="("&&b!="+"&&b!="-")                {                    Push(&S1,b);          //运算符存入到栈S1中                    Pop(&S2);                    if(S2.top!=-1)             //执行一次出栈                    {                       b=Get(S2);                    }                    else                    {                        break;                    }                }                Push(&S2,c);            }        }

(6)、当扫描完算术表达式的数组后,将栈S2中存储的操作符依次出栈并且进入到栈S1中,这时栈S1中的顺序是我们要求的后缀表达式的逆序,我们把这个逆序存入到一个数组中,将这个数组反向输出就是我们要求的后缀表达式。

    while(S2.top!=-1)      //将S2中剩余的操作符存入到栈S1中    {        char c=Get(S2);        Push(&S1,c);        Pop(&S2);    }    int a=S1.top;           //记录后缀表达式的长度    for(int i=0;S1.data[S1.top]!=-1;i++)   //将栈S1中的元素存入到数组中    {        data1[i]=S1.data[S1.top];        S1.top--;    }    for(int i=a;i>=0;i--)             //反向输出数组即可    {        printf("%c",data1[i]);    }

下图是李春葆书中给出的将算术表达式转换成后缀表达式的步骤:看懂我的或者是看懂下面这张图都可以。

三、例题应用——一般算术表达式转换成后缀式

Description

对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之。

Input

输入一个算术表达式,以‘#’字符作为结束标志。

Output

输出该表达式转换所得到的后缀式。

Sample

Input 

a*b+(c-d/e)*f#

Output 

ab*cde/-f*+

(1)栈的基本操作函数的定义

//定义一个栈结构体typedef struct{    char data[Maxsize];    //存储元素    int top;                //栈顶标记位}Stack;//初始化void Init(Stack *L){    L->top=-1;}//进栈操作void Push(Stack *L,char x){    if(L->top>=Maxsize)//判断是否栈满    {        return;    }    L->top++;    L->data[L->top]=x;      //栈没满就将x入栈}//出栈操作void Pop(Stack *L){    if(L->top==-1)//判断是否为空    {        return;    }    L->top--;        //不空就将栈顶标记位减一}//获取栈顶元素char Get(Stack L){    if(L.top==-1)    {        return 0;    }    else    {        return L.data[L.top];    }}

(2)主函数

主函数中的代码块的意思我已经在第二部分问题分析里面进行了详细的介绍,可以集合这代码理解那些话,逻辑理清楚就很简单。

int main(){    Stack S1;//存放最终的表达式    Stack S2;//存放运算符    Init(&S1);    Init(&S2);    char data[Maxsize];//存放用户输入的表达式    scanf("%s",data);    char data1[Maxsize];    char b;    for(int i=0;data[i]!="#";i++)    {        char c=data[i];        if(c=="(")        {            Push(&S2,c);        }        else if(c==")")        {            char b=Get(S2);//获取存操作符的栈顶元素            while(b!="(")//遇见左括号之前一直循环出栈S2            {                Push(&S1,b);//存入到栈S1                Pop(&S2);    //进行出栈S2                b=Get(S2);//再获取栈顶S2的元素            }            Pop(&S2);        }        else if(c!="+"&&c!="-"&&c!="*"&&c!="/"&&c!=")")//根据题目变换        {            Push(&S1,c);        }        else if(c=="+"||c=="-")        {            if(S2.top==-1)         //如果操作符栈为空,直接进栈            {                Push(&S2,c);            }            else            {                b=Get(S2);                while(b!="(")         //只要不为左括号或者是栈不为空,那就一直出栈                {                    Push(&S1,b);         //运算符存入到栈S1中                    Pop(&S2);                    if(S2.top!=-1)            //栈不为空,执行一次出栈                    {                        b=Get(S2);           //再获取栈顶                    }                    else                    {                        break;                    }                }                Push(&S2,c);            }        }        else if(c=="*"||c=="/")        {            if(S2.top==-1)            {               Push(&S2,c);            }            else            {                b=Get(S2);                while(b!="("&&b!="+"&&b!="-")                {                    Push(&S1,b);          //运算符存入到栈S1中                    Pop(&S2);                    if(S2.top!=-1)             //执行一次出栈                    {                       b=Get(S2);                    }                    else                    {                        break;                    }                }            }        }    }    while(S2.top!=-1)    {        char c=Get(S2);        Push(&S1,c);        Pop(&S2);    }    int a=S1.top;    for(int i=0;S1.data[S1.top]!=-1;i++)    {        data1[i]=S1.data[S1.top];        S1.top--;    }    for(int i=a;i>=0;i--)    {        printf("%c",data1[i]);    }    return 0;}

(3)运行结果

 (四)、完整代码

#include #include #define Maxsize 1000//定义一个栈结构体typedef struct{    char data[Maxsize];    //存储元素    int top;                //栈顶标记位}Stack;//初始化void Init(Stack *L){    L->top=-1;}//进栈操作void Push(Stack *L,char x){    if(L->top>=Maxsize)//判断是否栈满    {        return;    }    L->top++;    L->data[L->top]=x;      //栈没满就将x入栈}//出栈操作void Pop(Stack *L){    if(L->top==-1)//判断是否为空    {        return;    }    L->top--;        //不空就将栈顶标记位减一}//获取栈顶元素char Get(Stack L){    if(L.top==-1)    {        return 0;    }    else    {        return L.data[L.top];    }}int main(){    Stack S1;//存放最终的表达式    Stack S2;//存放运算符    Init(&S1);    Init(&S2);    char data[Maxsize];//存放用户输入的表达式    scanf("%s",data);    char data1[Maxsize];    char b;    for(int i=0;data[i]!="#";i++)    {        char c=data[i];        if(c=="(")        {            Push(&S2,c);        }        else if(c==")")        {            char b=Get(S2);//获取存操作符的栈顶元素            while(b!="(")//遇见左括号之前一直循环出栈S2            {                Push(&S1,b);//存入到栈S1                Pop(&S2);    //进行出栈S2                b=Get(S2);//再获取栈顶S2的元素            }            Pop(&S2);        }        else if(c!="+"&&c!="-"&&c!="*"&&c!="/"&&c!=")")//根据题目变换        {            Push(&S1,c);        }        else if(c=="+"||c=="-")        {            if(S2.top==-1)         //如果操作符栈为空,直接进栈            {                Push(&S2,c);            }            else            {                b=Get(S2);                while(b!="(")         //只要不为左括号或者是栈不为空,那就一直出栈                {                    Push(&S1,b);         //运算符存入到栈S1中                    Pop(&S2);                    if(S2.top!=-1)            //栈不为空,执行一次出栈                    {                        b=Get(S2);           //再获取栈顶                    }                    else                    {                        break;                    }                }                Push(&S2,c);            }        }        else if(c=="*"||c=="/")        {            if(S2.top==-1)            {               Push(&S2,c);            }            else            {                b=Get(S2);                while(b!="("&&b!="+"&&b!="-")                {                    Push(&S1,b);          //运算符存入到栈S1中                    Pop(&S2);                    if(S2.top!=-1)             //执行一次出栈                    {                       b=Get(S2);                    }                    else                    {                        break;                    }                }            }        }    }    while(S2.top!=-1)    {        char c=Get(S2);        Push(&S1,c);        Pop(&S2);    }    int a=S1.top;    for(int i=0;S1.data[S1.top]!=-1;i++)    {        data1[i]=S1.data[S1.top];        S1.top--;    }    for(int i=a;i>=0;i--)    {        printf("%c",data1[i]);    }    return 0;}

(五)、总结

        这道题让我做了一天,真的是我一点一点调试,在每一个条件里我都加上了一句打印语句,看一看是哪个条件出了问题,最后确定问题是出现在了当c=*或者是c=/这个判断上,当扫描到这个时,我们应该定义这个b!="("&&b!="+"&&b!="-"条件,而不是b!="("||b!="+"||b!="-",因为&&的意思是全真则为真,而||的意思是只要有一个为真就为真,就会执行条件里面的循环体。就比如我们拿一个例子:a+(b*c+d)#,当我们扫描到右括号)时,如果我们用的是b!="("||b!="+"||b!="-"这个条件,当我们扫描到*号时,我们直到此时操作符栈顶位左括号b=(,那我们用b!="("||b!="+"||b!="-"当作条件时,这个条件却是成立的,因为b此时确实不等于+,-。所以会执行里面的内容。但我们想表达的是当b=(,我们直接让*进栈,而不是让它出栈。

        虽然今天就做了这一道题,但真的是让我刻苦铭心,有所收获,设置判断条件时,一定要正面和反面一起考虑,只有条件设置对了我们的程序才能按照我们想让它执行的方向去执行。加油!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/124059.html

相关文章

  • 学习数据结构与算法栈与队列

    摘要:于是翻出了机房里的这本学习数据结构与算法开始学习程序员的基础知识。这本书用了我最熟悉的来实现各种数据结构和算法,而且书很薄,可以说是一本不错的入门教程。队列在头部删除元素,尾部添加元素。 本系列所有文章:第一篇文章:学习数据结构与算法之栈与队列第二篇文章:学习数据结构与算法之链表第三篇文章:学习数据结构与算法之集合第四篇文章:学习数据结构与算法之字典和散列表第五篇文章:学习数据结构与算...

    pingan8787 评论0 收藏0
  • Javascript数组系列一栈与队列

    摘要:所谓数组英语,是有序的元素序列。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。在栈中添加数据和删除数据也被称为推入和弹出,而且推入和弹出只会发生在栈的顶部。栈是一种数据结构,而队列则是一种的数据结构,即先进先出。 所谓数组(英语:Array),是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。 组成数组的各个变量称为数组的分量,也称...

    sunsmell 评论0 收藏0
  • 再识C语言(五)

    摘要:注不要移动负数位标准未定义行为这种行为属于标准未定义行为语言中并没有规定移动负数位。按进制位与规则两个二进制数,有则为,全则为。为假的时候,打印语言中表示假,非表示真无论是正数还是负数。 C语言操作符详解 目录 一、算术操作符 二、移位操作符 三、位操作符 四、赋值操作符 五、单目操作符 六...

    BigTomato 评论0 收藏0
  • Javascript数组系列五之增删改和强大的 splice()

    摘要:删除数组元素的开始索引需要删除元素的个数,插入数组的元素语法因为参数变化多样,我们主要从三个方面来展示的用法。 今天是我们介绍数组系列文章的第五篇,也是我们数组系列的最后一篇文章,只是数据系列的结束,所以大家不用担心,我们会持续的更新干货文章。 生命不息,更新不止! 今天我们就不那么多废话了,直接干货开始。 我们在《Javascript数组系列一之栈与队列》中描述我们是如何利用 pus...

    chavesgu 评论0 收藏0
  • 请回答c语言-操作符【入门】

    摘要:操作符的两个操作数必须为整数。函数调用用作为函数调用操作符。访问一个结构的成员结构体成员名结构体指针成员名还是熟悉的栗子在之前的博客请回答语言初识语言下入门的结构体出现过的栗子名字图鉴编号身高重量属性类型 ...

    frolc 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<