中缀转后缀实现字符串四则运算(C++)

字符串表达式求值的编程实现”

问题描述及求解过程

 

要求:给定一个含有字符 ‘0’-‘9’、小数点‘.’、加‘+’、乘‘*’、除‘/’、左括号‘(’、右括号‘)’以及空格的字符串,编写程序对其解析,并给出对应的数学表达式的值。

比如,对于字符串“(-3.5)+1.5/1.2*1.5”,计算结果应该为-1.625。

编程实现字符串表达式求值问题主要步骤如下:

1.解析输入字符串,将其转化为“中缀表达式”;

2.将“中缀表达式”转化为“后缀表达式”;

3.对转化得到的后缀表达式进行求值。

下面,将逐个分析各环节。

从输入到中缀表达式

“将输入的表达式字符串转化为中缀表达式”,这一步的主要工作有两个:

1.判断输入字符串的正确性及合法性;

2.将字符串分割成“原子”单位,即操作数(浮点数)和操作符,并依次压入某个栈中。

比如,字符串“(-3.5)+1.5/1.2*1.5”应该分割为:

( -3.5 ) + 1.5 / 1.2 * 1.5

其中,红色框与蓝色框分别指示其内容是“操作符”还是“操作数”。

一个表达式字符串是“合法”的,必须满足如下的条件:

1.‘+’、‘*’、‘/’以及‘-’作为减法操作符时,前后必须都是操作数。

2.操作数字符只能是‘0’-‘9’或者‘.’,规定负数必须有括号,例如(-3+5)*2、2*(-5)、(-5)*3合法,但是-3+5是不合法的。

3.每个操作数中‘.’最多只能出现一次。

4.操作数中不能出现空格,小数点后面不能出现空格,首字符不能含空格。

5.操作数与操作符之间、括号和操作数或操作符之间可以有空格。

6.操作数后面不能直接跟左括号‘(’。

7.左括号后不能直接跟‘+’、‘*’、‘/’、‘)’、‘.’,但能够跟(不超过一个)‘-’。

8.右括号后不能直接跟‘(’、‘.’、‘0’-‘9’。

9.整个表达式的左右括号数必须相等。

10.在表达式的任何位置,出现在其左边的左括号数不能小于出现在其左边的右括号数。

通过划分输入状态,得出下表:

当前状态state

输入字符char

下一状态nextstate

动作

op

"0,1,2...9"

non_dot

value+=char,polar=1

"+,-,*,/"

wrong

停机报错

"("

l_par

par_num++;Stack.push(char)

空格

non_dot

 

其它

wrong

停机报错

non_dot

"."

dot

value+=char

"+,-,*,/"

op

val=polar?atof(value):-atof(value);  Stack.push(val);        Stack.push(char);

"0,1,2...9"

non_dot

value+=char

"("

wrong

停机报错

空格

下一个非空格字符为"),+,-,*,/"时正确

 

其它

wrong

停机报错

")"

r_par

val=polar?atof(value):-atof(value);  Stack.push(val);       Stack.push(char);                   par_num--;若par_num<0,报错

dot

"0,1,2...9"

dot

value+=char,data=1

"."

wrong

停机报错

"+,-,*,/"且data=1

op

val=polar?atof(value):-atof(value);  Stack.push(val);        Stack.push(char);

")"且data=1

r_par

val=polar?atof(value):-atof(value);  Stack.push(val);         Stack.push(char);

其它

wrong

停机报错

l_par

"0,1,2...9"

non_dot

value+=char

"("

l_par

l_parnum++;Stack.push(char)

空格

non_dot

 

其它

wrong

停机报错

r_par

"+,-,*,/"

op

val=polar?atof(value):-atof(value);  Stack.push(val);        Stack.push(char);

")"

r_par

r_parnum++;

空格

r_par

 

其它

wrong

停机报错

space(伪状态,用于分析)

"."

wrong

 

" "空格

non_dot

进行比较判断

"+,-,*,/"

op

 

"("

l_par

l_parnum++;Stack.push(char)

")"

r_par

r_parnum--;

初始状态:l_par或non_dot

末尾操作:如果l_parnum!=r_parnum,报错,state=nextstate

 

根据上述状态,编写如下判定函数:

bool is_legal(string str);

数据结构设计

在上一部分的分析中,栈中有时需要存放操作数(浮点数),有时却需要存放操作符(字符、字符串)。考虑到数据类型的复杂性,在实现时采用OO的思想,声明一种自定义数据类型StackEle。该类中包含一个布尔类型的成员isop。该成员的值为true时,表明该元素为操作符;否则,该元素为操作数。

StackEle类的具体声明点击这里

接下来,为StackEle类派生两个子类DataStackEleOpStackEle。前者对应操作数元素,后者对应操作符元素。

于是,栈中的元素可以统一声明为StackEle类的指针。对于每一个DataStackEle类的指针,其isop需初始化为false,对于每一个OpStackEle类的指针,其is_op需初始化为true。

需要注意的是,考虑到我们需要使用StackEle类的指针访问其两个子类DataStackEle类和OpStackEle类的数据,因此在声明StackEle类时同时声明了getdata和getop两个虚函数。但是,在子类的声明中实现时,要有所区分。使用DataStackEle类的指针调用getop函数是没有意义的;反之,使用OpStackEle类的指针调用getdata函数也是没有意义的。

最后,声明一个堆栈类Stack用以存放指针。具体声明点击这里

中缀表达式到后缀表达式

由于运算符有不同的优先级级别,以及表达式中括号的存在,我们需要将中缀表达式转化为后缀表达式。此时我们可以将合法的输入式的内容压入一个栈stack中,此过程需要借助另一个临时栈op_stack。代码点击这里

注意:

  1. op_stack中只存放操作符,不存放操作数;
  2. 存在栈stack中的即为后缀表达式;
  3. stack中不会出现括号。

执行完毕后,即可将输入的表达式转化为后缀表达式形式:

-3.5 1 1.2 / 1.5 * +

后缀表达式求值

这是计算的最后一步,主要计算过程如下:首先,将stack中的内容逆向压入另一个栈rev_stack;接下来,复用栈stack完成求值。该步的主要特点为:

  1. stack中只存放操作数,不存放操作符;
  2. stack每次弹出两个操作符,再压入一个操作数;
  3. 过程结束时,stack中仅存放一个元素,其值恰为最终计算结果。
#include
#include
#include
using namespace std;

//堆栈元
class StackEle {
public:
	StackEle() { isop = false; }
	StackEle(bool n) { isop = n; }
	~StackEle() {};
	virtual double getdata()=0;
	virtual char getop()=0;
	virtual void setdata(double v) {}
	virtual void settype(char n) {}
protected:
	bool isop;
	
public:
	bool is_op() { return isop; }
	void set_isop(bool v) { isop = v; }

};
//存储运算数的堆栈

class DataStackEle :public StackEle {
public:
	DataStackEle() { data = 0; this->isop = false; }
	DataStackEle(double d) { data = d; this->isop = false; }//
	~DataStackEle() {}

private:
	double data;
public:
	double getdata() { return data; }
	char getop() { return 0; }
	void setdata(double v) { data = v; }
	};

class OpstackEle :public StackEle {
public:
	OpstackEle() { this->isop = true; }
	OpstackEle(char o) { type = o; this->isop = true; }//???
	~OpstackEle() {}
private:
	char type;
public:
	char getop() { return type; }
	double getdata() { return 0; }
	void settype(char n) { type = n; }
};
//存储运算式的堆栈
class Stack {
public:
	Stack() {
		Node =NULL;
	};
	Stack(StackEle * s) {
		Node = new node;
		Node->ele = s;
		Node->next = NULL;
	};
	~Stack() {
	while (Node != NULL)
	{
			node *N = Node;
			Node = Node->next;
			delete N;
	}

	}
protected:
	struct node
	{
		StackEle *ele;
		node *next;
	};
	node *Node;

public:
	node*pos(int n)
	{
		node* c = NULL;
		node* d = Node;
		int i = 0, m;
		m = count() - n - 1;
		while (inext;
			d = c;
		}
		return d;
	}
	int count() {
		int n = 0;
		node* c = Node;
		while (c)
		{
			n++;
			c = c->next;
		}
		return n;
	}
	void push(StackEle* s) {
		node *c;
		if (Node == NULL)
		{
			Node = new node;
			Node->ele = s;
			Node->next = NULL;
		}
		else
		{
			c = new node;
			c->ele = s;
			c->next = Node;
			Node = c;
			
		}
		
	};
	StackEle *pop() {
			StackEle *c;
			c = Node->ele;
			node *x = Node;
			if (Node->next != NULL)
			{
				Node = Node->next;
				delete x;
			}
			else
			{
				Node = NULL;
			}
			
			return c;
	};
	StackEle *top() {
		StackEle *c;
		if (Node != NULL)
		{
			c = Node->ele;
			return c;
		}
		else
		{
			return NULL;
		}
	};
	bool empty() {
		if (Node == NULL)
			return true;
		else
			return false;
	};
};
bool is_legal(string str);

int main()
{
	string S ;
	Stack stack,op_stack;//op_stack表示临时存储op的堆栈。stack 用于存储中缀表达式
	OpstackEle *op=NULL;//作为一个临时变量,用于建立堆栈节点
	DataStackEle *data=NULL;//作为一个临时变量,用于建立堆栈节点以及后续的计算
	Stack rev_stack;//用于转置
	StackEle *op_s=NULL, *data1=NULL, *data2=NULL;//用于接收临时op栈弹出的元素,方便查看
	int n;
	string num=" ";
	int i = 0;
	while (getline(cin, S))
	{
		i++;
		n = S.length();
		if (is_legal(S))
		{
			for (int i = 0; i < n; i++)
			{
				if (S[i] <= '9'&&S[i] >= '0'||S[i]=='.')
				{
					num += S[i];
				}
				else
				{
					if (num!=" ")
					{
						data = new DataStackEle;
						data->setdata(atof(num.data()));
						data->set_isop(false);
						stack.push(data);
						num = " ";
					}
					switch (S[i])
					{
					case '+':
					case '-':
					{
						if (!op_stack.empty())
						{
							op_s = op_stack.top();
							while (op_s->is_op() == true)
							{
								stack.push(op_stack.pop());
								if (op_stack.empty())
								{
									break;
								}
								else
									op_s = op_stack.top();
							}
						}
						op = new OpstackEle;
						op->settype(S[i]);
						op->set_isop(true);
						op_stack.push(op);
						break;
					}
					case '*':
					case '/':
					{
						
						if (!op_stack.empty())
						{
							op_s = op_stack.top();
							if (op_s->getop() == '*' || op_s->getop() == '/')
							{
								stack.push(op_s);
								op_stack.pop();
							}
						}
						op = new OpstackEle;
						op->settype(S[i]);
						op->set_isop(true);
						op_stack.push(op);
						break;
					}
					case'(':
					{
						if (S[i + 1] == '-')
						{
							num +='0';
						}
						op = new OpstackEle;
						op->settype(S[i]);
						op->set_isop(false);
						op_stack.push(op);
					}
					case ')':
					{
						if (!op_stack.empty())
						{
							op_s = op_stack.top();
							while (op_s->getop() != '(')
							{
								op_s = op_stack.pop();
								stack.push(op_s);
								if (op_stack.empty()==true)
								{
									break;
								}
							}
						}
						break;
					}
					default:
						break;
					}
				}				
			}
			if (num != " ")
			{
				data = new DataStackEle;
				data->setdata(atof(num.data()));
				data->set_isop(false);
				stack.push(data);
				num = " ";
			}
			while (!op_stack.empty())
			{
				StackEle *x = op_stack.pop();
				if(x->getop()!='(')
					stack.push(x);
			}
			
			while (!stack.empty())
			{
				StackEle *x = stack.pop();
				if (x->getop()!='(')
					rev_stack.push(x);
			}
			while (!rev_stack.empty())//计算结果
			{
				op_s = rev_stack.pop();
				if (op_s->is_op()==0)
				{
					stack.push(op_s);
				}
				else
				{
					data2 = stack.pop();
					data1 = stack.pop();
					double val;

					if (op_s->getop() == '+')
						val = data1->getdata() + data2->getdata();
					if (op_s->getop() == '-')
						val = data1->getdata() - data2->getdata();
					if (op_s->getop() == '*')
						val = data1->getdata() * data2->getdata();
					if (op_s->getop() == '/')
						val = data1->getdata() / data2->getdata();
					
					data1->setdata (val) ;
					data1->set_isop(false);
					stack.push(data1);
				}
			}
		}
		else
		{
			cout << "第"<getdata()<= '1'&&str[0] <= '9') || str[0] == '('||str[0]=='-'||str[0]==' ')
	{		
		for (int i = 0; i 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(程序设计)