目录

1. 实验内容与要求 3

1.1. 实验内容 3

1.2. 实验要求 3

2. 设计思路分析 3

2.1. 系统总体设计

2.2. 系统功能定义 4

2.3. 算法设计 7

2.3.1. 中缀表达式转逆波兰 7

2.3.2. 逆波兰表达式的计算 10

3. 功能实现 12

3.1. STL栈链的定义 12

3.2. 小数的提取 16

3.3. 整数的提取 18

3.4. 优先级的实现 21

3.5. 取余操作的实现 25

4. 功能截图 26

4.1. 转换中缀表达式 26

4.2. 计算逆波兰式 28

4.3. .报错功能... 29

5. 问题和解决方法 34

5.1. 34

5.2 34

5.3...........................................................................................................................................................34

5.4...........................................................................................................................................................35

5.5...........................................................................................................................................................35

5.6...........................................................................................................................................................35

6. 课程设计感想 37

7. 源代码 37

  1. 实验内容与要求

    1.  实验内容

逆波兰表达式又叫做后缀表达式,是波兰逻辑学家 J・卢卡西维兹于 1929 年首先提出的

一种表达式的表示方法。采用这种表达式组织逻辑提问非常方便检索运算,所以这种方法最

早用于情报检索。不同于通常的中缀表达式,逆波兰表达式把操作数写在前面,操作符写在

后面,所以也称为后缀表达式。请采用相应的数据结构实现中缀表达式转换成逆波兰表达式,

并实现逆波兰表达式的计算,要求能够支持加、减、乘、除、取余、指数和括号等运算。

  1.  实验要求

1. 要求采用链表来实现栈,不允许使用标准模板类的链表类(list)、栈类(stack)和函数;

2. 要求支持加、减、乘、除、取余、指数和括号等运算;

3. 实现中缀表达式转换成逆波兰表达式的功能时,要求输出显示能够清楚地显示出的整个

转换过程,包括压栈和出栈过程;

4. 实现逆波兰表达式的计算功能时,要求输出显示能够清楚地显示出的整个计算过程,包

括压栈和出栈过程;

5. 要求采用类的设计思路,不允许出现类以外的函数定义,但允许友元函数。主函数中只

能出现类的成员函数的调用,不允许出现对其它函数的调用。

6. 要求采用多文件方式:.h 文件存储类的声明,.cpp 文件存储类的实现,主函数 main 存

储在另外一个单独的 cpp 文件中。如果采用类模板,则类的声明和实现都放在.h 文件中。

7. 不强制要求采用类模板,也不要求采用可视化窗口;要求源程序中有相应注释;

8. 建议采用 Visual C++ 6.0 及以上版本进行调试;

  1. 设计思路分析

    1. 系统总体设计

一:程序设计时的初步想法:

1.利用栈“先进后出”特征实现逆波兰的计算及转换的算法。

2.主要考虑优先级问题(计算)和报错问题(计算和转换)。

3.采用先报错,后计算规则,有错则立即终止,不参与计算。

4.采用STL模板栈链的结构:转为逆波兰用一个LinkedStack<char>类型栈,存储运算符。计算用一个LinkedStack<double>类型栈,存储操作数。都是利用栈“先进后出”的特征实现算法。

5.支持+,-,*,/,^,%,(,)这些操作符的运算;

6.转化和计算都使用switch提取数字和运算符。

7.利用if控制优先级的问题。

8.实现较好的交互功能:用输入参数的方法进行交互。

二.程序设计时的进一步想法:

1.在中缀表达式转换为逆波兰表达式的过程中,先在转换的初步过程中判断表达式的正确性,正确则再计算得到的逆波兰式表达式的值判断中缀表达式的正确性,正确则输出转换步骤及转换结果,下面是较详细的介绍:

初步转换中缀表达式的函数返回值为string类型,正常情况下返回得到的逆波兰表达式,若返回了“fault”,则表达式错误了,在此过程中不会输出转换步骤。若在此过程中不报错,则进一步用判断逆波兰表达式的函数计算可以正常返回的逆波兰表达式,返回类型为bool类型,true则表明中缀表达式是正确的,false则表明是错误的。若经过前两个步骤表达式均判断正确,则输出转换步骤和最终结果。

2.在逆波兰式计算过程中,先在计算的初步过程中判断逆波兰表达式的正确性,正确则输出计算步骤及最终计算结果,下面是详细介绍:

初步判断逆波兰表达式的函数返回的是bool类型,为true则表明逆波兰表达式输入无误,可以参与计算,为false则表明输入有误,在此过程中不会输出计算的步骤。若输入无误,用计算逆波兰表达式的函数输出计算步骤和计算结果。

3.STL中有五个友元函数:①初步转换中缀表达式②初步判断中缀转换的逆波兰表达式③转化中缀表达式④初步计算并判断逆波兰表达式⑤计算逆波兰表达式的函数,均设为LinkedStack的友元函数。(前三个为转换过程中需要用到的函数,后两个为计算逆波兰表达式中需要用到的函数)

4.要求可以支持小数和多位整数参与运算(正确输入形式:小数之间和两位数以上的整数得用空格隔开)。

  1. 系统功能定义

功能清单及其简要备注

功能描述

输入

输出

备注

中缀转为逆波兰式

中缀表达式

逆波兰字符串(string 类型)

先转换,转换中查找是否输入正确,再通过计算值判断输入是否正确。

计算逆波兰式的值

逆波兰表达式

计算的结果(double类型)

直接在计算中查找是否有错。

用栈实现主要功能:

①.中缀表表达式转为逆波兰表达式的转换时栈的变化

和逆波兰式的变化:

②.利用栈计算逆波兰表达式的值的例子:

  1. 算法设计

一.中缀转为逆波兰的设计

1.简要流程图

2.判断中缀表达式是否有初级错误:

(在转换过程中报错)

能检测的错误有:

①.右括号能否准确对应每个左括号,不能对应则马上报错,终止转换( 例如错误表达式“(1+2))”)

②.左括号是否多于右括号,多则报错,用户自己决定是否重新输入。(例如错误表达式“((1+3)”)

③.是否输入了不合法的符号(除了计算符和括号和数字之外的符号),有则马上报错,提示最前面的错误,终止转换。

④输入表达式左括号左边是数值,右括号右边是数值的问题:例如“1(+2)”或者“(1+4)7+”。判断出错误则立刻停止转换。

3.判断中缀表达式是否有语义错误:

(在计算逆波兰式值的过程中报错)若转换的逆波兰式在计算时是有错误的,则这个中缀表式也是有错误的。

能检测的错误有:

①.数字输入错误:小数点连着输入并中间无符号隔开(例如12.22.3或者12...)

②.小数输入小数点后紧接着输运算符:判断小数点后的下一位是否是数字,不是则报错,并提示相应错误,(例如错误12.+或者12.())

③.除数是否为0:判断在一步步转换的过程中,是否存在这种状况,则马上报错,并终止转换。(例如“3/0”或者“3/(2+1-3)”  )

④.是否将小数进行%运算:判断的是数字本身是不是小数,而不是单纯的小数类型。有则马上报错并终止转换。(例如“2.3%2”或者“2%2.3”或者“2.3%2.5”)注:4.000%2.000为合法表达式,不报错。

⑤.运算符输入过多,而计算值已经没有了:马上报错并终止转换。(例如错误2+或者2+4+等)

⑥.运算符混乱等问题:例如1++2,马上报错并终止。

⑧.括号多但是不影响计算时不报错,例如表达式   3/()(2.33+5.6*5)。

4.合法中缀表达式的转换,一边输出转换步骤,一边转换。

①逆波兰式储存post(string初始为空);

②辅助string  NO='';string BLANK=" ";

③运算符储存栈stack类型 opstack

二.逆波兰式计算的设计

1.逆波兰计算简要流程图:

2.判断逆波兰式是否输入有错误:

先在在计算过程中判断语义错误和输入错误

能检测的错误有:

①.数字输入错误:小数点连着输入并中间无符号隔开(例如12.22.3或者12...)

②.小数输入小数点后紧接着输运算符:判断小数点后的下一位是否是数字,不是则报错,并提示相应错误,(例如错误12.+或者12.())

③.除数是否为0:判断在一步步转换的过程中,是否存在这种状况,则马上报错,并终止转换。(例如“3 0 /”或者“3 2 1+3-/”  )

④.是否将非整数进行%运算:判断的是数字本身是不是小数,而不是单纯的小数类型。有则马上报错并终止转换。(例如“2.3 2%”或者“2 2.3%”或者“2.3 2.5%”).  注:4.000%2.000为合法表达式,不报错。 程序会将其转换为整数类型计算,将计算结果返回为0(double类型);

⑤.运算符输入过多,而计算值已经没有了:马上报错并终止转换。(例如错误2+或者2+4+等)

⑥.输入了不合法的符号:在数字中间,运算符中间,表达式前或者表达式后均可以查找错误。例如(“2.3@”或者“2.3+@+”或者“@@2.3 3.4+”或者“2.3 4.2+@”);

⑦.未输入任何计算的数:(例如“+”或者“/”等错误);

3.计算正确的逆波兰表达式的值:一边输出计算步骤,一边转换。

  

3.功能实现

3.1栈链类实现:

采用表格的形式描述类的定义和功能

栈链类在Class.h中

表2  栈链( LinkedStack)类

类名称: LinkedStack

功能: 提供一个基本的栈链用来支持运算

#pragma once

#ifndef    CLASS_H

#define    CLASS_H

#include <iostream>

#include <string>

using namespace std;

//--------------------------------------------------------------------

template<typename T>                    //节点定义

struct node {

node(T x) { nodeValue = x; next = 0; }

T nodeValue;//数据域

node* next; //指针域

};

//------------------------------------------------------------------------

template<typename T>                     //栈链的定义

class LinkedStack

{

public:

LinkedStack() { count = 0; Header = 0; }

void  push(T& a);

void pop();

T& top();

bool empty();

int size();                                  //友元函数的定义

friend string zhuan(string exp);           //初步判断中缀表达式的是否正确

friend bool zhongjisuan(string exp);        //进一步判断中缀表达式是否正确

friend string postfix(string exp);           //转换正确的中缀表达式

friend   bool  panduan(string exp);      //判断逆波兰表达式是否正确

friend  double jisuan(string exp);       //计算正确的逆波兰表达式的值

private:

node<T>* Header;

int count;

};

//------------------------------------------------内部函数的实现-------------------

template<typename T>

void LinkedStack<T>::push(T& item)

{

node<T>* newnode = new node<T>(item);

newnode->next = Header;

Header = newnode;

count++;

}

template<typename T>

void LinkedStack<T>::pop()

{

node<T>* tmp = Header;

Header = Header->next;

delete tmp;

count--;

}

template<typename T>

T& LinkedStack<T>::top()

{

return Header->nodeValue;

}

template<typename T>

bool LinkedStack<T>::empty()

{

return count == 0;

}

template<typename T>

int LinkedStack<T>::size()

{

return count;

}

#endif

3.2小数的提取:

3.2.1转换过程的小数提取设计想法:

若遇到整数,直接把该整数逐位加入逆波兰式中,但是需要注意整数连在一起,中间不能有任何字符隔开,用NO表示不含空格的空串。

若遇到小数:

则这个小数的数值分为两种:①小数的第一位②小数点前的数值③小数点后的数值

若为小数第一位(判断前面是否为运算符,考虑到运算符和数字之间没有用空格隔开)则把数值用空格隔开加到逆波兰式中,用BLANK表示只含有一个空格的串。

若当前是小数点前的数值,则逐一加入逆波兰式中。

若当前为小数点后第一位数值(通过判断当前位置的前一个是不是小数点,已经把小数点存入了逆波兰式中),则进入循环,直到把小数点后所有数值加到逆波兰式中。

功能: 提取小数

位置: 初步转换中缀表达式为逆波兰表达式

需要用到的一些参数:

/*char token, top, pre = ',', pre1 = '1', pre2 = ',';     //token表示当前字符,top栈顶

LinkedStack<char> opstack;

string post;

const string BLANK = " ";                                //用于使数字之间用空格隔开

const string NO = "";  */

//若为小数,则碰到小数点就把小数点加入到逆波兰式中

case'.':post.append(NO + token);                    //遇到小数点则直接加到逆波兰式中(中间不能被空格隔开)

if (i >= 1) pre1 = exp[i - 1];                   //判断小数点前面是否为数字

else return"fault";                              //若小数点为中缀表达式的首字符,则这个表达式一定错误

if (pre1 > '9' || pre1 < '0')                   //若小数点前不是数字,则返回并报错

{

cout << "小数输入不完整" << endl;

return "fault";

}

pre1 = '1';                                     //置为1

break;

if (token >= '0' && token <= '9')                         //toke表示当前为数字

{

int j;

j = i;

if (i >= 1) pre = exp[i - 1];

if (pre == '.')   //若存在小数点,则这个操作数为小数 ,把小数点后的数值全部提取出来

{

while (exp[j] >= '0' && exp[j] <= '9' && j < exp.length())  //则一直找到这个小数结束为止

{

token = exp[j];

post.append(NO + token);    //把该操作数小数点后的数值加到逆波兰式中

j++;

}

i = j - 1;

}

else if (pre == '+' || pre == '-' || pre == '*' || pre == '/' || pre == '%' || pre == '^' || pre == '(' || pre == ')'||pre == ' ')

{

post.append(BLANK + token);      //若当前数字前面是运算符,或者括号,用空格隔开

}

//否则则说明当前数字前也是数字,不用空格隔开加到逆波兰式中

else post.append(NO + token);

break;

}

3.2.2计算逆波兰过程的小数提取设计想法:

①若在计算逆波兰式时,遇到了数字,则一直提取完数值。

②用k1计算小数点在整个数值中出现的位置,j记录该数值有几位数(小数点后的位数也算)。在数值提取过程中先不把小数点放进去,提取完数值后再把小数点放到原位置,通过a = a / (pow(10, j - k1)),还原原小数数值,再把操作数压入操作数栈。

③提取过程中,也可以判断一些错误:小数之间未用空格断开/小数输入不完整。

④涉及逆波兰式计算提取小数的方法均类似,但也有些差异,因为判断过了表达式的正确性就不用判断小数是否会输入错误。这里放了初步计算逆波兰表达式函数中的小数提取过程,含判错。

功能: 提取小数

位置: 计算逆波兰表达式函数中(初步计算,伴随着判断错误)

需要用到的参数

/*LinkedStack<double>operStack;

char token, back;

double a, b, c, d, e, topToken, x1, x2;  */

token = exp[i];                    //表示为当前逆波兰式的第i位置

if (token >= '0' && token <= '9')                         //若当前为数字

{

if (i + 1 < exp.length()) back = exp[++i];   //需要判断后一个字符是否为小数点

else return false;                   //若数字是最后一个字符,该表达式绝对错误

a = token - 48;                   //把字符转换为double类型

int j = 0, k1 = 0, k2 = 0;       //k2记录数字输入时有没有输入错误(两个小数点连一起)

while (back != ' ' && i < exp.length() && back != '+' && back != '-' && back != '*' && back != '/' && back != '%' && back != '^')

{                                                //一直提取小数,直到提取完

j++;

if (back == '.') { k1 = j; k2++; }             //k1记录小数点的位置,

else if (back >= '0' && back <= '9') { e = back - 48; a = a * 10 + e; }  //数字先提取

else { return false; }      //判断数值之间是否有不合法的字符

if (i + 1 < exp.length()) back = exp[++i];          //i往后移

else return false;

}

if (k2 > 1 || (j == k1 && j != 0)) { cout << "错误:小数之间未用空格断开/小数输入不完整" << endl; return false; }//判断错误

if (k1 != 0) a = a / (pow(10, j - k1));                       //将小数点恢复原位

k1 = j = 0;                                                  //重置为0

operStack.push(a);                                           //数值压入运算数栈

i--;                                                         //继续判断当前字符

}

3.3整数的提取:

3.3.1转换过程的整数提取设计想法:

若该操作数为整数:数值分为①整数的第一位②整数的中间位置③整数的末位置

若为整数的第一位:若前面为空格,则会执行先用空格(BLANK)隔开,把该数值放入逆波兰表达式。

若为整数的中间数值:不用空格隔开(NO)放入逆波兰式中。

功能: 提取整数

位置: 初步转换中缀表达式函数中(初步转换,伴随着判断错误)

需要用到的一些参数:

/*char token, top, pre = ',', pre1 = '1', pre2 = ',';     //token表示当前字符,top栈顶

LinkedStack<char> opstack;

string post;

const string BLANK = " ";                                //用于使数字之间用空格隔开

const string NO = "";  */

if (token >= '0' && token <= '9')                         //若当前为数字

{

int j;

j = i;

if (i >= 1) pre = exp[i - 1];                       //判断当前数字的前一个是否为小数点

if (pre == '.')                                   //若为小数点

{。。。。。。。。。       /省略该代码

}

else if (pre == '+' || pre == '-' || pre == '*' || pre == '/' || pre == '%' || pre == '^' || pre == '(' || pre == ')'||pre == ' ')

{

post.append(BLANK + token);      //若当前数字前面是运算符,或者括号,用空格隔开

}

//否则则说明当前数字前也是数字,不用空格隔开加到逆波兰式中

else post.append(NO + token);

3.3.2计算过程的整数提取设计想法:

若为整数,则也不会包含小数点,直接一步步提取。通过判断k1?=0,判断是否为整数,为0则该操作数为整数。提取完所有数值后,直接把该整数压入栈(虑到了代码的通用性,所以是小数提取的代码放一起的)。

其他计算逆波兰函数中提取整数的代码类似,稍有不同,这里放了初步计算逆波兰式表达式函数中提取整数的代码。

功能: 提取整数

位置: 初步计算逆波兰表达式函数中(伴随判错)

需要用到的参数

/*LinkedStack<double>operStack;

char token, back;

double a, b, c, d, e, topToken, x1, x2;  */

token = exp[i];                    //表示为当前逆波兰式的第i位置

if (token >= '0' && token <= '9')                         //若当前为数字

{

if (i + 1 < exp.length()) back = exp[++i];   //需要判断后一个字符是否为小数点

else return false;                   //若数字是最后一个字符,该表达式绝对错误

a = token - 48;                   //把字符转换为double类型

int j = 0, k1 = 0, k2 = 0;       //k2记录数字输入时有没有输入错误(两个小数点连一起)

while (back != ' ' && i < exp.length() && back != '+' && back != '-' && back != '*' && back != '/' && back != '%' && back != '^')

{                                                //一直提取小数,直到提取完

j++;

if (back == '.') { k1 = j; k2++; }             //k1记录小数点的位置,

else if (back >= '0' && back <= '9') { e = back - 48; a = a * 10 + e; }  //数字先提取

else { return false; }      //判断数值之间是否有不合法的字符

if (i + 1 < exp.length()) back = exp[++i];          //i往后移

else return false;

}

if (k2 > 1 || (j == k1 && j != 0)) { cout << "错误:小数之间未用空格断开/小数输入不完整" << endl; return false; }//判断错误

if (k1 != 0) a = a / (pow(10, j - k1));                       //将小数点恢复原位

k1 = j = 0;                                                  //重置为0

operStack.push(a);                                           //数值压入运算数栈

i--;                                                         //继续判断当前字符

}

3.4优先级的实现:

操作符优先级的问题只在转换中缀表达式中存在。

3.4.1优先级问题设计想法:

①遇到左括号,则直接把左括号压入运算符栈。

②遇到右括号,则弹出操作符栈中的运算符,放到逆波兰式中,并用空格隔开(BLANK)直到遇到左括号。

③栈空,则把操作符直接压入栈。

④运算符栈栈顶为左括号,则直接把操作符压入栈。

⑤若当前运算符优先级高于栈顶运算符优先级,则直接把运算符压入运算符栈

条件为:1.当前运算符为:*,/,%  栈顶为:+,-

2.当前运算符为:^ 栈顶为:*,/,%,+,-

不符合条件则弹出操作符,弹出栈顶优先级高于当前运算符的运算符,用空格隔开加入到逆波兰表达式中。直到当前运算符的优先级高于栈顶运算符。

⑥所有都存入完毕后,把运算符栈中剩余的操作符弹出

功能: 优先级问题,把操作符放到逆波兰式的合适位置

位置: 把中缀表达式转换为逆波兰表达式(初步转换)(伴随判错)

需要用到的参数:

int zuo = 0, you = 0;                    //用来记录左右括号的数量,以便正确输出错误

char token, top, pre = ',', pre1 = '1', pre2 = ',';     //token表示当前字符,top栈顶

LinkedStack<char> opstack;

string post;

const string BLANK = " ";                                //用于使数字之间用空格隔开

const string NO = "";                                    //保证小数的完整

token = exp[i];

switch (token) {

case' ':

//省略无关代码

break;

case'.':

//省略无关代码

break;

case'(':                                              //若遇到左括号

opstack.push(token); zuo++;                      //直接压入栈,左括号数+1

if (i >= 1) pre2 = exp[i - 1];                  //判断左括号前面是否为数字

if (pre2 >= '0' && pre2 <= '9' && i >= 1)       //若左括号前面是数字,则报错并返回

{

cout << "您输入有问题,左括号左边不应该是数字" << endl; return "fault";

}

pre2 = ',';                                        //置为,

break;

case')':                                               //若遇到右括号

if (i + 1 < exp.length()) pre2 = exp[i + 1];       //判断右括号右边是否为数字

if (pre2 >= '0' && pre2 <= '9')                    //若为数字,则报错

{

cout << "您输入有问题,右括号右边不应该是数字" << endl; return "fault";

}

pre2 = ',';                                    //置为,

you++;                                          //由括号数+1

for (;;)

{                                               //若碰到右括号,运算符的栈中一直弹出运算符直至遇到左括号

if (!opstack.empty())

{

top = opstack.top();

opstack.pop();

}                                              //若一直找到栈空都找不到,说明只有右括号,没有对应左括号,报错并返回

else { cout << "----------------右括号对应错误,无法转换!!!!-------------" << endl; return "fault"; }

if (top == '(') break;                         //退回到了左括号则退出循环

post.append(BLANK + top);                     //把运算符用空格隔开加到逆波兰式末尾

}

break;

case'+':case'-':

case'*':case'/':case'%': case'^':                          //若当前为运算符

for (;;) {

if (opstack.empty() ||                             //若运算符栈是空的,或者栈顶为左括号,或者当前运算符比栈顶运算符优先级高

opstack.top() == '(' ||

(token == '*' || token == '/' || token == '%') &&

(opstack.top() == '+' || opstack.top() == '-'))

{

opstack.push(token);                           //直接把运算符压入运算符栈中

break;

}

else if (token == '^' && (opstack.top() == '*' || opstack.top() == '/' || //当前运算符等级高于栈顶运算符

opstack.top() == '%' || opstack.top() == '+' ||

opstack.top() == '-'))

{

opstack.push(token);                             //直接把运算符压入栈

break;

}

else

{                                                 //否则弹出栈顶运算符,加到逆波兰式中

top = opstack.top();

opstack.pop();

post.append(BLANK + top);

}

}

break;

弹出剩余操作符,返回是也需要判断左右括号数是否被相等,是否对应在前面的代码中会判断。

while (!opstack.empty()) {

top = opstack.top();

opstack.pop();

if (top != '(')                              //若不是左括号,则加到逆波兰式中,并用空格隔开

{

post.append(BLANK + top);

}

}

if (zuo != you)                                  //若左右括号不对应,则报错.会产生问题

{

cout << "--------------左括号有" << zuo << "个   右括号有" << you << "个!!!  表达式的括号不对应!!!!!   输入有误--------" << endl;

return "fault";

}

3.5整数取余操作的实现:

因为操作数栈存储的是double类型,而double类型的是不能用于取余操作的,所以要把为整数的操作数转换为int 类型,进行取余操作,小数无法进行取余操作。

把double类型的操作数转为int类型,若两个数值相等,则说明该数为整数,则可以进行取余操作,不相等则说明该数为小数,不能进行取余操作。

下面是初步计算中缀表达式转换的初步逆波兰式中整数取余操作。

功能:整数取余

位置:计算初步转换的逆波兰式的值(含判错)

需要用到的参数的定义

Double a, b, c, d, topToken, x1, x2;

int e = 0;

int x, y, z;

case'%':if (operStack.empty()) { return false; }

x = b = operStack.top(); x1 = x - b;

operStack.pop();

if (operStack.empty()) { return false; }

y = c = operStack.top(); x2 = y - c;

operStack.pop();

z = y % x;

d = z;

if (x1 != 0 || x2 != 0) cout << "------错误!!-------取余操作不能用于非整数,输入错误!!!!" << endl;

else {

operStack.push(d);

}

break;

4. 功能运行截图:

4.1.中缀转为逆波兰的功能截图(输入为正确的中缀表达式)

输入 1   2.33*(4+6)^1.2+4%3/2+3-(6+8)      (1表示是选择了菜单中转换中缀表达式的选项)

涉及关键代码:

getline(cin, infixexp);                        //读入输入的字符串                                 

infixexp.erase(0, 1);                  //去掉输入的菜单选项,和中间的空格

infixexp.erase(0, infixexp.find_first_not_of(" "));

运行截图

 

4.2.逆波兰表达式计算的功能截图(输入为正确的逆波兰表达式)

输入:2   2.33 4 6 + 1.2 ^ * 4 3 % 2 / + 3 + 6 8 + -;

(2表示是选择了菜单中计算逆波兰式的选项)

涉及关键代码:

getline(cin, shuru);        //获取输入的字符串

shuru.erase(0, 1);         //去掉逆波兰式前面的菜单选项和前面的空格

shuru.erase(0, shuru.find_first_not_of(" "));

4.3.1错误表达式的输入,报错响应(转换中缀表达式):

①:右括号能否准确对应每个左括号。

输入:(1*4.55+6.7)),需要报错。

②:左括号是否多于右括号,多则报错。

输入:((1+2),

虽然这种情况下不影响计算,但是如果不报错,在涉及优先级等问题时就会出错。

③:存在不合法的字符:

输入:2.33+344.55$44.55

输入:@12.33+45.5

输入:23.44 +44.4!

④:小数输入错误:

输入:45+44/(12....)+5

输入:28.33.44+4.55*3

输入:6.+4.5

⑤:括号数字问题

左括号左边不会是数字,右括号右边不会是数字。

这个问题是要报错的,因为不添加报错的话它还是可以正常转换的,但是表达式是错误的。

输入:34.4(+9)

输入:32.33+(2.36-5.6)7+

⑥.除数为0

输入 3/(7-7)

除数不能为0,不然表达式无意义,不能转换。

⑦double取余问题:

整数是可以正常取余的,要把double类型的整数转为int类型。

输入:3.00%2.00      可以正常运算

小数不可以取余,是无意义的,要报错。

输入 4.3%4

⑧.运算符过多问题。

运算符过多,则后面计算时找不到数进行运算,需要报错。

4.3.2错误表达式的输入,报错响应(计算逆波兰式):

①.小数输入错误

输入:12.......4.5+

输入:12.444 3.33.44 +

②.除数为0

分为显式错误和隐式错误。

输入: 3 0 /     ,输入: 3 2 1+3-/

③.double类型取余操作

double类型的整数转换为int类型可以正常取余,小数不可以

输入:4.0000 3.0000%    表达式正确,可以正常计算并输出步骤

输入:2.3 2%

      2 1.5%

均错误,均报错。

④.逆波兰式中存在不合法符号

因为无法计算,需要报错。

例如输入:2.3@    ,输入:@4.5 6+

⑤.操作符过多问题

操作符多,无法运算

例如

输入:2.3 4.5 ++

输入:2.3 4.8+ 3.55 - /

⑥.无输入数字(运算符过多)

输入:+

5.问题和解决方法

问题主要涉及报错功能。

5.1

问题描述:提取小数的问题,当小数输入正确时 ,是可以正常提取的,但一旦多输了几个小数点,程序会认为小数已经结束,提取下一个数值,但是后面的数值其实是无法分辨的,所以这个时候必须返回并报错。

解决方法:加了一个参数k2,用于记录小数提取完时是不是多于一个小数点,多则说明输入是有错误的。则马上返回并报错。

5.2

问题描述:刚开始设计时,想的是每一个功能都加一个判错功能的函数,是想实现和计算器一样,输入有错则不计算报错的功能,但是在转换中缀表达式为逆波兰表达式时,是按照字符注意读取,读完了就结束,它本身是无法判断一些重要的语义错误,例如3/(2+1-3),2.22%3.4它只是一个转换的函数,是无法在返回到前面的数值判断错误的,然而这类错误是必须要报出来的。

解决方法:在转换中缀表达式后,又加了一个计算逆波兰式的函数,专门计算由中缀表达式初步转换来的逆波兰式,在该函数中加一些报错的代码,能计算正确,则说明输入无误,得不出就计算结果,则报错,必定是输入有误的原因。

5.3

问题描述:实现了小数也可以转逆波兰,并且可以计算,所以输出的结果要定义为double类型,但是就算是double类型的整数,也是不能进行取模运算的,这在计算机中是不允许的,所以取模这部分的代码是不能通过测试的。

解决方法:分别定义两个int类型的数,在取余运算时,用来传递double类型的数值,若两两之间的差为0,可以认为这两个操作数均为正数,是可以进行取余操作的,程序正常运行并输出结果,若有一个差值不为0,则说明取余操作涉及了小数,返回并报错。这样正数之间的取余操作便可以正常进行了

5.4

问题描述:由输入参数来控制选择菜单导致输入的表达式被拓宽,和输入菜单错误导致陷入无限循环

解决方法:程序需要实现较好的交互功能,本实验还涉及两个主要的功能,那必须通过用户的输入来选择功能,但是由于读取表达式时是使用getline函数,会把前面的菜单选项也读进去,可以通过

infixexp.erase(0, 1);   infixexp.erase(0, infixexp.find_first_not_of(" ")); 去掉输入的菜单选项,和中间的空格。

但是这里也会涉及一个需要报错的问题,因为要考虑到用户输入菜单时就输入格式有误和输入其他不存在的菜单选项,本来是想加一个else来控制报错,但是因为原代码中会一直有cin>>x,x一直

保持着原来的值,所以一直在执行else,陷入死循环。其实如果只是输入了错误的数字是可以正常报错的,但是输入的是字母和一些符号等,就还是会有问题。有尝试过用while(cin.fail)来控制报错,但是不知道还是控制不了,干脆不写这个了,这也是程序的一个问题,就是输入格式必须正确,不然程序会出错,只能在程序运行的时候特别提示请勿错误输出。思考后,我觉着用图形界面的实现方法比较好。但是时间有限,来不及学C++图形界面的实现。

5.5

问题描述:STL栈链的内部函数实现问题

解决方法:实验要求是把定义的类函数的申明放在.h文件中,其他实现函数放在.cpp中,但是在vs

调试过程过,只要把它的内部函数放在.cpp中实现,就会持续报错,研究了很久,也不知道是哪里出来问题,格式也都是正确的,最后我把内部的实现函数也放在了.h中,程序就可以正常运行了,所以我把内部函数也放在了.h头文件中,猜测是因为写了模板类的原因。

5.6

问题描述:在Dev c++中运行原代码是没问题的,放在Visual studio里报错了,因为下标溢出问题。

解决方法:因为很多报错功能都涉及取前一个字符或者后一个字符判断表达式的准确性,所以就会有下标为-1,或者超出了字符串的长度的情况,所以需要在每个函数涉及取单个字符操作的时候都判断下表的合法性,同时不影响其他功能的实现,还是需要考虑挺多问题的,一个一个测试来判断代码是否有该改对。

6.实验体会与心得

7.原代码

7.1头文件Class.h

#pragma once

#ifndef    CLASS_H

#define    CLASS_H

#include <iostream>

#include <string>

using namespace std;

//--------------------------------------------------------------------

template<typename T>                    //节点定义

struct node {

node(T x) { nodeValue = x; next = 0; }

T nodeValue;//数据域

node* next; //指针域

};

//------------------------------------------------------------------------

template<typename T>                     //栈链的定义

class LinkedStack

{

public:

LinkedStack() { count = 0; Header = 0; }

void  push(T& a);

void pop();

T& top();

bool empty();

int size();                                  //友元函数的定义

friend string zhuan(string exp);           //初步判断中缀表达式的是否正确

friend bool zhongjisuan(string exp);        //进一步判断中缀表达式是否正确

friend string postfix(string exp);           //转换正确的中缀表达式

friend   bool  panduan(string exp);      //判断逆波兰表达式是否正确

friend  double jisuan(string exp);       //计算正确的逆波兰表达式的值

private:

node<T>* Header;

int count;

};

//------------------------------------------------内部函数的实现-------------------

template<typename T>

void LinkedStack<T>::push(T& item)

{

node<T>* newnode = new node<T>(item);

newnode->next = Header;

Header = newnode;

count++;

}

template<typename T>

void LinkedStack<T>::pop()

{

node<T>* tmp = Header;

Header = Header->next;

delete tmp;

count--;

}

template<typename T>

T& LinkedStack<T>::top()

{

return Header->nodeValue;

}

template<typename T>

bool LinkedStack<T>::empty()

{

return count == 0;

}

template<typename T>

int LinkedStack<T>::size()

{

return count;

}

#endif

7.2main.cpp

#include "Class.h"

#include <iostream>

#include <string>

#include <sstream>

#include <cmath>

using namespace std;

//-----------------------------------------------------------------------友元函数的实现

//----------------------------------------------------------------------初步把后缀表达式转为逆波兰式

string zhuan(string exp) {

int zuo = 0, you = 0;                                   //用来记录左右括号的数量,以便正确输出错误

char token, top, pre = ',', pre1 = '1', pre2 = ',';     //token表示当前字符,top栈顶

LinkedStack<char> opstack;

string post;

const string BLANK = " ";                                //用于使数字之间用空格隔开

const string NO = "";                                    //保证小数的完整

for (int i = 0; i < exp.length(); i++) {

token = exp[i];

switch (token) {

case' ':

if (i >= 1) pre = exp[i - 1];                    //判断空格前面是否就是小数点,若正确应该为运算符和数字

if (pre == '.')                                  //若为小数点,则说明小数输入不完整

{

cout << "小数输入不完整" << endl;

return "fault";

}

break;

case'.':post.append(NO + token);                    //遇到小数点则直接加到逆波兰式中(中间不能被空格隔开)

if (i >= 1) pre1 = exp[i - 1];                   //判断小数点前面是否为数字

else return"fault";                              //若小数点为中缀表达式的首字符,则这个表达式一定错误

if (pre1 > '9' || pre1 < '0')                   //若小数点前不是数字,则返回并报错

{

cout << "小数输入不完整" << endl;

return "fault";

}

pre1 = '1';                                     //置为1

break;

case'(':                                              //若遇到左括号

opstack.push(token); zuo++;                        //直接压入栈,左括号数+1

if (i >= 1) pre2 = exp[i - 1];                    //判断左括号前面是否为数字

if (pre2 >= '0' && pre2 <= '9' && i >= 1)         //若左括号前面是数字,则报错并返回

{

cout << "您输入有问题,左括号左边不应该是数字" << endl; return "fault";

}

pre2 = ',';                                        //置为,

break;

case')':                                               //若遇到右括号

if (i + 1 < exp.length()) pre2 = exp[i + 1];       //判断右括号右边是否为数字

if (pre2 >= '0' && pre2 <= '9')                    //若为数字,则报错

{

cout << "您输入有问题,右括号右边不应该是数字" << endl; return "fault";

}

pre2 = ',';                                    //置为,

you++;                                          //由括号数+1

for (;;)

{                                               //若碰到右括号,运算符的栈中一直弹出运算符直至遇到左括号

if (!opstack.empty())

{

top = opstack.top();

opstack.pop();

}                                              //若一直找到栈空都找不到,说明只有右括号,没有对应左括号,报错并返回

else { cout << "----------------右括号对应错误,无法转换!!!!-------------" << endl; return "fault"; }

if (top == '(') break;                         //退回到了左括号则退出循环

post.append(BLANK + top);                     //把运算符用空格隔开加到逆波兰式末尾

}

break;

case'+':case'-':

case'*':case'/':case'%': case'^':                          //若当前为运算符

for (;;) {

if (opstack.empty() ||                             //若运算符栈是空的,或者栈顶为左括号,或者当前运算符比栈顶运算符优先级高

opstack.top() == '(' ||

(token == '*' || token == '/' || token == '%') &&

(opstack.top() == '+' || opstack.top() == '-'))

{

opstack.push(token);                           //直接把运算符压入运算符栈中

break;

}

else if (token == '^' && (opstack.top() == '*' || opstack.top() == '/' || //当前运算符等级高于栈顶运算符

opstack.top() == '%' || opstack.top() == '+' ||

opstack.top() == '-'))

{

opstack.push(token);                             //直接把运算符压入栈

break;

}

else

{                                                 //否则弹出栈顶运算符,加到逆波兰式中

top = opstack.top();

opstack.pop();

post.append(BLANK + top);

}

}

break;

default:

if (token >= '0' && token <= '9')                         //若当前为数字

{

int j;

j = i;

if (i >= 1) pre = exp[i - 1];                       //判断当前数字的前一个是否为小数点

if (pre == '.')                                     //若为小数点

{

while (exp[j] >= '0' && exp[j] <= '9' && j < exp.length())  //则一直找到这个小数结束为止

{

token = exp[j];

post.append(NO + token);                              //把小数加到逆波兰式中

j++;

}

i = j - 1;

}

else if (pre == '+' || pre == '-' || pre == '*' || pre == '/' || pre == '%' || pre == '^' || pre == '(' || pre == ')'||pre==' ')

{

post.append(BLANK + token);      //若当前数字前面是运算符,或者括号,用空格隔开

}

//否则则说明当前数字前也是数字,不用空格隔开加到逆波兰式中

else post.append(NO + token);

break;

}

else                                  //若中缀表达式中,出现了非运算符(+,-,*,/,^,%),非数字,非空格,非有效括号,则输入有误

{

cout << "---------------中缀表达式第" << i + 1 << "位输入符号有错误" << endl;

return "fault";

}

}             //switch结束

}             //for循环结束

//弹出栈中剩余的运算符

while (!opstack.empty()) {

top = opstack.top();

opstack.pop();

if (top != '(')                              //若不是左括号,则加到逆波兰式中,并用空格隔开

{

post.append(BLANK + top);

}

}

if (zuo != you)                                  //若左右括号不对应,则报错.会产生问题

{

cout << "--------------左括号有" << zuo << "个   右括号有" << you << "个!!!  表达式的括号不对应!!!!!   输入有误--------" << endl;

return "fault";

}                                                     //初步判断没有问题则返回初步转换的逆波兰式

return post;

}

//----------------------------------------------------------------------友元函数的实现

//----------------------------------------------------------------------计算中缀初步转换后的逆波兰,返回bool类型,用于判断中缀表达式是否输入错误

bool  zhongjisuan(string exp) {

LinkedStack<double>operStack;

char token, back;

double a, b, c, d, e, topToken, x1, x2;

int x, y, z;

for (int i = 0; i < exp.length(); i++) {

token = exp[i];

if (token >= '0' && token <= '9')                         //若当前为数字

{

if (i + 1 < exp.length()) back = exp[++i];          //需要判断后一个字符是否为小数点

else return false;                                   //若数字是最后一个字符,该表达式绝对错误

a = token - 48;

int j = 0, k1 = 0, k2 = 0;                             //k2记录数字输入时有没有输入错误(两个小数点连一起)

while (back != ' ' && i < exp.length() && back != '+' && back != '-' && back != '*' && back != '/' && back != '%' && back != '^')

{                                                       //一直提取小数,直到提取完

j++;

if (back == '.') { k1 = j; k2++; }                 //k1记录小数点的位置,

else if (back >= '0' && back <= '9') { e = back - 48; a = a * 10 + e; }  //数字先提取

else { return false; }      //判断数值之间是否有不合法的字符

if (i + 1 < exp.length()) back = exp[++i];          //i往后移

else return false;

}

if (k2 > 1 || (j == k1 && j != 0)) { cout << "错误:小数之间未用空格断开/小数输入不完整" << endl; return false; }//判断错误

if (k1 != 0) a = a / (pow(10, j - k1));                       //将小数点恢复原位

k1 = j = 0;                                                  //重置为0

operStack.push(a);                                           //数值压入运算数栈

i--;                                                         //继续判断当前字符

}

else {

switch (token) {

case' ':break;

case'+':if (operStack.empty()) { return false; }             //若一个运算符前不能提出两个操作数,则一定有错误

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = c + b;                                            //将运算出来的数值压入操作数栈

operStack.push(d);

break;

case'-':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = c - b;

operStack.push(d);

break;

case'*':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = c * b;

operStack.push(d);

break;

case'/':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();                                                     //判断除数是否为0

if (b == 0) { cout << "------错误!!--------存在除数为0的错误,输入错误!!!!" << endl; return false; }

d = c / b;

operStack.push(d);

break;

case'%':if (operStack.empty()) { return false; }

x = b = operStack.top(); x1 = x - b;

operStack.pop();

if (operStack.empty()) { return false; }

y = c = operStack.top(); x2 = y - c;                //小数之间不能进行取余操作

operStack.pop();

z = y % x;                                         //若只是小数格式的整数,则还是可以进行运算的

d = z;                                             //小数则报错

if (x1 != 0 || x2 != 0) cout << "------错误!!-------取余操作不能用于小数,输入错误!!!!" << endl;

else {

operStack.push(d);

}

break;

case'^':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = pow(c, b);

operStack.push(d);

break;

default:break;

}

}

}

if (operStack.empty()) { return false; }                        //若栈空,则说明没有返回结果,则报错

else return true;

}

//----------------------------------------------------------------------友元函数的实现

//----------------------------------------------------------------------返回中缀表达式转换后的逆波兰式,并输出步骤

string postfix(string exp) {

char token, top, pre = ',';

LinkedStack<char> opstack;

string post;

const string BLANK = " ";

const string NO = "";

for (int i = 0; i < exp.length(); i++) {

token = exp[i];

cout << endl << "----现在是中缀表达式的第" << i << "位  " << token << endl;

switch (token) {

case' ':break;

case'.':post.append(NO + token); cout << "----遇到小数点,逆波兰式变为" << post << endl; break;

case'(':opstack.push(token); cout << "----遇到了 (  直接压入栈" << endl; break;

case')':

for (;;)

{

//assert(!opstack.empty());

top = opstack.top();

opstack.pop();

cout << "----遇到)" << top << "出栈" << endl << endl;

if (top == '(') break;

post.append(BLANK + top);

cout << "----逆波兰式变为" << post << endl;

}

break;

case'+':case'-':

case'*':case'/':case'%': case'^':

for (;;) {

if (opstack.empty() ||

opstack.top() == '(' ||

(token == '*' || token == '/' || token == '%') &&

(opstack.top() == '+' || opstack.top() == '-'))

{

opstack.push(token);

cout << "----遇到" << token << "   压入栈" << endl;

break;

}

else if (token == '^' && (opstack.top() == '*' || opstack.top() == '/' ||

opstack.top() == '%' || opstack.top() == '+' ||

opstack.top() == '-'))

{

opstack.push(token);

cout << "----遇到" << token << "   压入栈" << endl;

break;

}

else

{

top = opstack.top();

opstack.pop();

cout << "----遇到" << token << "      " << top << "  出栈------------" << endl;

post.append(BLANK + top);

cout << "----逆波兰式变为" << post << endl;

}

}

break;

default:

if (token >= '0' && token <= '9')

{

int j;

j = i;

if (i >= 1) pre = exp[i - 1];

if (pre == '.')

{

while (exp[j] >= '0' && exp[j] <= '9' && j < exp.length())

{

token = exp[j];

cout << "----现在是中缀表达式的第" << j << "位  " << token << endl;

post.append(NO + token);

j++;

}

i = j - 1;

}

else if (pre == '+' || pre == '-' || pre == '*' || pre == '/' || pre == '%' || pre == '^' || pre == '(' || pre == ')'||pre == ' ')

{

post.append(BLANK + token);

}

else post.append(NO + token);

cout << "----遇到数字,逆波兰式变为" << post << endl;

break;

}

}             //switch结束

}                //for循环结束

cout << "--------------------该表达式中的数字均已经弹出--------------" << endl;

while (!opstack.empty()) {

top = opstack.top();

opstack.pop();

if (top != '(')

{

post.append(BLANK + top);

cout << "--------------------还有运算符弹出,逆波兰式变为" << post << endl;

}

}

return post;

}

//--------------------------------------------------------------------友元函数的实现

//--------------------------------------------------------------------判断输入的逆波兰,返回bool类型,用于判断逆波兰表达式是否输入错误

bool  panduan(string exp) {                                //虽然前面也有通过逆波兰式的是否可以正常运算判断输入式子的准确性,

//但是由于中缀表达式已经判断基本的输入错误,而直接计算逆波兰的值时,

LinkedStack<double>operStack;                          //是没有判断基本输入错误的(输入非法字符),所以这两个函数不能通用,虽然功能差不多

char token, back;

double a, b, c, d, topToken, x1, x2;

int e = 0;

int x, y, z;

for (int i = 0; i < exp.length(); i++) {

token = exp[i];

if (token >= '0' && token <= '9')

{

if (i + 1 < exp.length()) back = exp[++i];

else return false;                               //这步主要用于判断输入放入逆波兰是否错误

a = token - 48;

int j = 0, k1 = 0, k2 = 0;                      //k2记录数字输入时有没有输入错误(两个小数点连一起)

while (back != ' ' && i < exp.length() && back != '+' && back != '-' && back != '*' && back != '/' && back != '%' && back != '^')

{

j++;

if (back == '.') { k1 = j; k2++; }

else if (back >= '0' && back <= '9') { e = back - 48; a = a * 10 + e; }

else { return false; }

if (i + 1 < exp.length()) back = exp[++i];

else return false;

}

if (k2 > 1 || (j == k1 && j != 0)) { cout << "错误:小数之间未用空格断开/小数输入不完整" << endl; return false; }

if (k1 != 0) a = a / (pow(10, j - k1));

k1 = j = 0;                                                    //重置为0

operStack.push(a);

i--;

}

else {

switch (token) {

case' ':break;

case'+':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = c + b;

operStack.push(d);

break;

case'-':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = c - b;

operStack.push(d);

break;

case'*':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = c * b;

operStack.push(d);

break;

case'/':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

if (b == 0) { cout << "------错误!!--------存在除数为0的错误,输入错误!!!!" << endl; return false; }

d = c / b;

operStack.push(d);

break;

case'%':if (operStack.empty()) { return false; }

x = b = operStack.top(); x1 = x - b;

operStack.pop();

if (operStack.empty()) { return false; }

y = c = operStack.top(); x2 = y - c;

operStack.pop();

z = y % x;

d = z;

if (x1 != 0 || x2 != 0) cout << "------错误!!-------取余操作不能用于非整数,输入错误!!!!" << endl;

else {

operStack.push(d);

}

break;

case'^':if (operStack.empty()) { return false; }

b = operStack.top();

operStack.pop();

if (operStack.empty()) { return false; }

c = operStack.top();

operStack.pop();

d = pow(c, b);

operStack.push(d);

break;

default:return false;                                           //需要判断是否有非法字符

}

}

}

if (operStack.empty()) { return false; }

else return true;

}

//--------------------------------------------------------------------友元函数的实现

//--------------------------------------------------------------------计算逆波兰式的值,并输出步骤

double jisuan(string exp) {

LinkedStack<double>operStack;

char token, back;

double a, b, c, d, e, topToken;

int x, y, z;

for (int i = 0; i < exp.length(); i++) {

token = exp[i];

if (token >= '0' && token <= '9')

{

if (i + 1 < exp.length()) back = exp[++i];

a = token - 48;

int j = 0, k1 = 0;

while (back != ' ' && i < exp.length() && back != '+' && back != '-' && back != '*' && back != '/' && back != '%' && back != '^')

{

j++;

if (back == '.') { k1 = j; }

else if (back >= '0' && back <= '9')

{

e = back - 48; a = a * 10 + e;

}

if (i + 1 < exp.length()) back = exp[++i];

}

if (k1 != 0) a = a / (pow(10, j - k1));

k1 = j = 0;                                  //重置为0

operStack.push(a);

cout << "提取到数字-------" << a << "---------并压入栈" << endl;

i--;

}

else {

switch (token) {

case' ':break;

case'+':

b = operStack.top();

operStack.pop();

c = operStack.top();

operStack.pop();

d = c + b;

operStack.push(d);

cout << "------" << c << "和" << b << "加法运算变为" << d << "  同时" << d << "压入栈" << endl;

break;

case'-':

b = operStack.top();

operStack.pop();

c = operStack.top();

operStack.pop();

d = c - b;

operStack.push(d);

cout << "------" << c << "和" << b << "减法运算变为" << d << "  同时" << d << "压入栈" << endl;

break;

case'*':b = operStack.top();

operStack.pop();

c = operStack.top();

operStack.pop();

d = c * b;

operStack.push(d);

cout << "------" << c << "和" << b << "乘法运算变为" << d << "  同时" << d << "压入栈" << endl;

break;

case'/':b = operStack.top();

operStack.pop();

c = operStack.top();

operStack.pop();

d = c / b;

operStack.push(d);

cout << "------" << c << "和" << b << "除法运算变为" << d << "  同时" << d << "压入栈" << endl;

break;

case'%':

x = b = operStack.top();

operStack.pop();

y = c = operStack.top();

operStack.pop();

z = y % x;

d = z;

operStack.push(d);

cout << "------" << c << "和" << b << "取余运算变为" << d << "  同时" << d << "压入栈" << endl;

break;

case'^':

b = operStack.top();

operStack.pop();

c = operStack.top();

operStack.pop();

d = pow(c, b);

operStack.push(d);

cout << "------" << c << "和" << b << "幂运算变为" << d << "  同时" << d << "压入栈" << endl;

break;

default:break;

}

}

}

topToken = operStack.top();

return topToken;

}

//---------------------------------------------------------------------菜单函数的实现

void menu() {

cout << endl << endl << endl;

cout << "                   ---***---***------------欢迎进入逆波兰----****----****---菜单选项如下:---****----------------------------" << endl << endl << endl;

cout << "                   |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|" << endl;

cout << "                   |                                                                                |" << endl;

cout << "                   |---***---***------------中缀表达式转为逆波兰请输入-----1+(空格)+你要转换的中缀表达式-------****----****|" << endl;

cout << "                   |---***---***-----------------逆波兰式计算请输入--------2+(空格)+你要计算的逆波兰表达式-----****----****|" << endl;

cout << "                   |---***---***----------------- 退出系统请按输入---------3-------------------------------------****----****|" << endl;

cout << "                   |                                                                                                         |" << endl;

cout << "                   |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|" << endl << endl << endl;

cout << "----------------------注意:输入格式不要错误!!!! 尤其是选完选项后,输入表达式时,一定要空格隔开!!!---------------------" << endl;

}

//---------------------------------------------------------------------主函数的实现

int main() {

string infixexp, pre, xx, x1;

int c;

string shuru;

bool f1;

double a;

for (;;)

{

menu();

cin >> c;

if (c == 1) {

cout << "---------------------------中缀转逆波兰计算-----------------------" << endl;

getline(cin, infixexp);

infixexp.erase(0, 1);                  //去掉输入的菜单选项,和中间的空格

infixexp.erase(0, infixexp.find_first_not_of(" "));

pre = infixexp;                        //记录初试字符串

x1 = zhuan(infixexp);                  //先初步转换

//cout<<x1<<endl;

if (x1 == "fault") {                   //判断是否有初步错误

cout << "--------------------------您存在输入错误,无法转换-----------------------" << endl;

continue;

}

if (!zhongjisuan(x1)) {               //判断是否有更多错误

cout << "-----------------您有错误输入!!计算机无法转化!!!!----------------" << endl;

}

else {                              //无错就输出转换步骤

xx = postfix(infixexp);

cout << pre << "该中缀表达式转为逆波兰后为" << xx << endl;

}

c = 0;

}

else if (c == 2)

{

cout << "----------------------逆波兰的计算-------------------" << endl;

getline(cin, shuru);

shuru.erase(0, 1);

shuru.erase(0, shuru.find_first_not_of(" "));

pre = shuru;

f1 = panduan(shuru);

cout << endl << endl;

if (!f1) {

cout << "------------------经过判断,您输入有误,计算机无法计算---------- 您是否重新输入" << endl;

continue;

}

else {

a = jisuan(shuru);

cout << pre << "----------------------最终计算结果变为--------------" << a << endl;

}

c =0;

}

else if (c == 3) {

exit(0);

}

}

}

数据结构课程设计——逆波兰表达式的计算相关推荐

  1. 西工大NOJ数据结构理论——021.逆波兰表达式(严7.38)

    这道题我参考的是(80条消息) 『西工大-数据结构-NOJ』 021-逆波兰表达式(耿7.38) 『西北工业大学』__LanXiu的博客-CSDN博客 (准确来说是快期末考试了,所以各科老师都在疯 狂 ...

  2. c语言小数表达式运算课程设计,数据结构课程设计表达式计算.doc

    数据结构课程设计表达式计算 福建农林大学计算机与信息学院 计算机类 课程设计报告 课程名称:算法与数据结构课程设计题目:表达式计算姓 名:系:数学系专 业:数学与应用数学年 级:学 号:指导教师:宁正 ...

  3. 表达式求值问题数据结构课程设计

    完整代码在最后~~ 1 需求分析 1.1 问题描述 表达式求值是程序设计语言编译中的一个最基本问题,就是将一个表达式转化为逆波兰表达式并求值.具体要求是以字符序列的形式从终端输入语法正确的.不含变量的 ...

  4. 数据结构 - 栈 (逆波兰计算器)(栈的三种表达式)(前缀、中缀和后缀表达式,后缀也叫逆波兰表达式)(中缀表达式转后缀表达式实现步骤及完整代码)

    栈的三种表达式:前缀.中缀和后缀表达式,后缀也叫逆波兰表达式 前缀(波兰表达式) 中缀(对人来讲很好理解,对于计算机来讲就方便了,一般会把中缀表达式转换成后缀表达式) 后缀(逆波兰表达式) 计算过程 ...

  5. php逆波兰表达式,PHP实现逆波兰式 - 计算工资时用

    近期一个小项目需要用到公式运算, 所以就进行一些了解,以下内容均属于个人经验. 在PHP中实现公式表达式四则运算大概有两种方法: 1)使用系统函数eval 2)将表达式转换成逆波兰表达式进行计算. / ...

  6. 前缀表达式后缀表达式_你知道波兰表达式和逆波兰表达式吗

    什么是波兰表达式 我们日常的运算表达式通常是如下形式,这种成为中缀表达式,也就是运算符在运算数的中间.这种表达式人类很容易识别,并根据其进行计算,但计算机识别这种表达式非常困难. a + b * (c ...

  7. 调度场算法与逆波兰表达式

    本文的主要内容是如何求一个给定的表达式的值,具体思路就是先将普通算术的中缀表达式转化为后缀表达式,这一步用到的算法叫做调度场算法.然后对后缀表达式,也就是逆波兰表达式求值. 题目:http://acm ...

  8. nyoj35——逆波兰表达式

    逆波兰表达式又称作后缀表达式,在四则混合运算的程序设计中用到. 例如: 1+2写成后缀表达式就是12+ 4+5*(3-2)的后缀表达式就是4532-*+ 后缀表达式在四则运算中带来了意想不到的方便,在 ...

  9. 数据结构-栈应用之逆波兰表达式(后缀表达式)

    逆波兰表达式含义我就不做赘述了,摘自百科上的一段话: 逆波兰表达式又叫做后缀表达式.在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示.波兰逻辑学家J.Lukas ...

  10. 设树采用孩子兄弟表示法存放.用类c语言设计算法计算树的高度.,(数据结构课程设计分类题目.doc...

    (数据结构课程设计分类题目 线性表 顺序表: 1.设有一元素为整数的线性表L=(a1,a2,a3,-,an),存放在一维数组A[N]中,设计一个算法,以表中an作为参考元素,将该表分为左.右两部分,其 ...

最新文章

  1. JavaWeb--过滤器
  2. 清华大学计算机系2015分数线,清华大学2015-2017高考录取分数线,附各省录取数据...
  3. HTML+CSS的学习
  4. jquery监听滚动条
  5. Oracle快速备份表数据
  6. Mysql索引机制B+Tree
  7. 零基础也能看懂!数据仓库与数据库的这几个问题,你能回答出来吗
  8. JQuery树插件——ztree
  9. 关于生成静态页--终极解决方案
  10. Java:使用 Java 开发的一个异常处理框架
  11. wireshark https_测开日常积累-wireshark应用
  12. java序列化 反序列化_Java序列化– Java序列化
  13. 【搜索入门】桐桐的组合
  14. SQL 必知必会·笔记6使用数据处理函数
  15. 莱布尼茨公式C语言编程,高等数学——手撕牛顿莱布尼茨公式
  16. 说的特别好的一句话,送给每一个热爱编程的人
  17. 关于《算法(第四版 谢路云译)》标准库In、Out、StdOut和StdIn的正确配置和调用经验分享(以BinarySearch二分查找算法为例)
  18. robotframework基础入门:(3):找不到关键字的对应方法
  19. 教师节HTML祝福网页,教师节祝福信息模板
  20. Roberts边缘检测算子

热门文章

  1. 蓝桥杯:座次问题(枚举法 回溯) java
  2. 路由器WIFI密码怎么设置?快速设置,只需1分钟
  3. vue中使用(瀑布流)vue-waterfall-easy插件
  4. java qua_Qua Vadis Eclipse? 第二部分
  5. Qua Vadis Eclipse? 第一部分
  6. 解决魅族手机不输出Log日志 或者输出部分日志问题
  7. 可重入锁模拟三个线程之间的通信
  8. 网页播放全景视频和全景图片
  9. 三进制与八进制相互转换
  10. torch 显存管理