您的位置:首页 > 编程语言 > C语言/C++

简单算术表达式C++实现

2015-10-06 20:03 701 查看
输入的算术表达式是中缀表达式,由于操作符的优先级不同,不能直接进行求解,而后缀表达式的计算不需要判别操作符的优先级,所以我实现算术表达式的思路是:1)应用堆栈将中缀表达式转化为后缀表达式,2)用堆栈实现后缀表达式求值。

用堆栈实现后缀表达式求值的过程:从左到右读入后缀表达式的各项,并根据读入的对象判断执行何种操作,有以下3中情况:

1) 当读入的是一个运算数时,把它压入栈中;

2) 当读入的是一个运算符时,就从栈中弹出适当数量的运算数并进行计算,计算结果再压回到栈中;

3) 处理完整个后缀表达式之后,栈顶上的元素就是表达式的结果。

应用堆栈将中缀表达式转换为后缀表达式的基本过程为:从头到尾读取中缀表达式的每个对象,对不同对象按不同情况处理:

1) 如果遇到空格则认为是分隔符,无需处理;

2) 若遇到运算数,则直接输出;

3) 若是左括号,则将其压入至栈中;

4) 若遇到的是右括号,表明括号内的中缀表达式已经扫描完毕,将栈顶的运算符弹出并输出,直到遇到左括号(左括号也出栈,但不输出);

5) 若遇到的是运算符,若该运算符的优先级大于栈顶运算符的优先级,则把它压入栈中;若该运算符的优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出,在比较新的栈顶运算符,按这样的处理方法,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;

6) 若中缀表达式中的各对象处理完毕,则把堆栈中保留的运算符一并输出。

在代码实现的过程中,首先我们要定义一个栈,因为在后缀表达式求值的过程中,栈是用来存运算数的;在中缀转换为后缀表达式的过程中,栈是用来存放操作符的。所以在实现栈的时候我用了模板。栈的实现如下:

// Stack.h

#include<vector>
template <class T>
class Stack{
public:
void push(const T&);
T pop();
T peek();//该函数只是返回最顶端的元素,并不删除
bool empty();
private:
std::vector<T> _stack;
};
template<class T>
void Stack<T>::push(const T& a){
_stack.push_back(a);
}
template<class T>
T Stack<T>::pop(){
//在要用到出栈的时候一定要保证栈不为空
auto it = _stack.end();
auto c = *(it - 1);
_stack.pop_back();
return c;
}
template<class T>
bool Stack<T>::empty(){
return _stack.begin() == _stack.end();
}
template<class T>
T Stack<T>::peek(){
auto it = _stack.end();
return *(it - 1);
}


定义相应的运算符的优先级,我用整数的大小来定义优先级的大小,对应的函数实现如下:

int op_pri(const char& a){
switch (a){
case ')':
return 0;
case '+':case '-':
return 1;
case '*':case'/':
return 2;
case '(':
return 3;
}
}


在实现优先级的时候,当输入为左括号时,需要无条件进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以右括号的优先级我定义为最低。但是要注意当当前的运算符为右括号时,证明括号内的中缀表达式已经扫描完毕,要将栈顶运算符弹出,直到左括号(这时左括号出栈,但是不用加入到运算中);还要注意当运算符是左括号时,因为我把左括号定义为了最高优先级了,如果按正常的情况,当栈顶是左括号时,应当会马上输出但是这不符合我们的要求,希望的是紧接的运算符能无条件的入栈,所以在代码的实现过程中这是中特殊的情况用个if语句强调。

接下来是对中缀转换为后缀表达式实现的最主要的功能函数,就是运算符的出入栈的情况:

int oper_cp(const char&a, const char& b){

int c = op_pri(a);
int d = op_pri(b);
if (c == 0 && d == 3)
return -1;//当左括号碰到右括号时,定义为一个特殊状态,此状态是直接把栈中的左括号操作符弹出
//右括号也不进栈
else if (d == 3)//定义优先级时,以整数大小来对应优先级的大小。但是当输入为左括号时,需要无条件
//进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以
//右括号的优先级要最低。
//但是在右括号进栈后,若紧接的操作符是+、-、*、/的话,需要进栈,这个条件语句就是在这种情况下
//对应的操作符直接进栈
return 1;
else if (c > d){
return 1;
}
else
return 0;

}


有了上述两个函数的支持,接下来就是直接对中缀表达式求值的函数,这些问题只是逻辑问题,我的代码实现如下:

void exp(Stack<double> &val, Stack<char> &op){
char c{};
string s{};
do
{
c = getchar();

if ((c >= '0'&&c <= '9') || c == '.')
{
s.push_back(c);
continue;
}
else
{
if (s != "\0")
{
val.push(atof(s.c_str()));
s = "\0";
}

}
if (c == '+' || c == '-' || c == '*' || c == '/' || c == ')' || c == '(')
{
char b;
if (op.empty()){
op.push(c);
continue;
}
while (1)
{
if (!op.empty())
b = op.peek();
else
{
op.push(c);
break;
}

int m = oper_cp(c, b);
if (m == 1)
{
op.push(c);
break;
}
if (m == 0)
{
b = op.pop();
double c, d;
d = val.pop();
c = val.pop();
switch (b)
{
case '+':
val.push(c + d);
break;
case '-':
val.push(c - d);
break;
case'*':
val.push(c*d);
break;
case'/':
val.push(c / d);
break;
}
continue;
}
if (m == -1)
{
op.pop();
break;
}
}
}
} while (c != '\n');
while (!op.empty()){
char b = op.pop();
double e, d;
d = val.pop();
e = val.pop();
switch (b)
{
case '+':
val.push(e + d);
break;
case '-':
val.push(e - d);
break;
case'*':
val.push(e*d);
break;
case'/':
val.push(e / d);
break;
}
}

}


调用这些函数的main函数如下:

#include<iostream>
#include<string>
#include "Stack.h"
using namespace std;
void exp(Stack<double> &val, Stack<char> &op);
int op_pri(const char& );
int oper_cp(const char&a, const char& b);
int main(){
Stack<double> val;
Stack<char> op;
exp(val, op);
cout << val.pop();
}

int oper_cp(const char&a, const char& b){ int c = op_pri(a); int d = op_pri(b); if (c == 0 && d == 3) return -1;//当左括号碰到右括号时,定义为一个特殊状态,此状态是直接把栈中的左括号操作符弹出 //右括号也不进栈 else if (d == 3)//定义优先级时,以整数大小来对应优先级的大小。但是当输入为左括号时,需要无条件 //进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以 //右括号的优先级要最低。 //但是在右括号进栈后,若紧接的操作符是+、-、*、/的话,需要进栈,这个条件语句就是在这种情况下 //对应的操作符直接进栈 return 1; else if (c > d){ return 1; } else return 0; }
int op_pri(const char& a){ switch (a){ case ')': return 0; case '+':case '-': return 1; case '*':case'/': return 2; case '(': return 3; } }
void exp(Stack<double> &val, Stack<char> &op){ char c{}; string s{}; do { c = getchar(); if ((c >= '0'&&c <= '9') || c == '.') { s.push_back(c); continue; } else { if (s != "\0") { val.push(atof(s.c_str())); s = "\0"; } } if (c == '+' || c == '-' || c == '*' || c == '/' || c == ')' || c == '(') { char b; if (op.empty()){ op.push(c); continue; } while (1) { if (!op.empty()) b = op.peek(); else { op.push(c); break; } int m = oper_cp(c, b); if (m == 1) { op.push(c); break; } if (m == 0) { b = op.pop(); double c, d; d = val.pop(); c = val.pop(); switch (b) { case '+': val.push(c + d); break; case '-': val.push(c - d); break; case'*': val.push(c*d); break; case'/': val.push(c / d); break; } continue; } if (m == -1) { op.pop(); break; } } } } while (c != '\n'); while (!op.empty()){ char b = op.pop(); double e, d; d = val.pop(); e = val.pop(); switch (b) { case '+': val.push(e + d); break; case '-': val.push(e - d); break; case'*': val.push(e*d); break; case'/': val.push(e / d); break; } } }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: