分别用C++和JavaScript 实现四则运算表达式求值
2016-04-22 15:46
302 查看
博主16年4.19去面腾讯实习生,其中一个问题是让写一个函数求四则运算表达式的值,输入是字符串,输出为表达式结果。当时只记得这是数据结构里堆栈的应用,表达式要变顺序,但是实现就想不起来了,自然程序写的一塌糊涂,结果也就呵呵了。回来之后翻书查资料把程序写出来,供大家参考。程序主要思路参考CSDN中作者wzfxyer 的文档
点击打开链接 。
首先,明确两个个概念,“中缀表达式”和 ”后缀表达式“一般的运算表达式。例如”9+(3-1)*3+10/2“,这是中缀表达式,而”9 3 1- 3 *+10 2 / +“则是后缀表达式,计算后缀表达式的值,先把数字入栈,遇到操作符则栈顶两个数字出栈运算,然后再把结果存入栈中,比如,计算该后缀表达式的过程为
,①9,3,1入栈,遇到‘-’,计算3-1=2,将2入栈,此时栈中为9,2。②接着3入栈,栈中为9,3,2,然后遇到‘*’,栈顶两个数字3,2出栈计算3*2=6,将6入栈,此时栈中为9,6。③接着遇到‘+’号,栈顶两元素出栈,计算9+6=15,将15入栈,此时栈中为15。④,然后10,2,入栈,最后遇到‘/’,栈顶两数字10,2出栈计算,10/2 =5,并将5入栈,此时栈中为15,5。⑤遇到操作符‘+’,将栈顶两元素弹 出,计算15+5 = 20.入栈再出栈,即为最后结果。
所以计算表达式的值有两个关键步骤,一是将中缀表达式转为后缀表达式;二是计算后缀表达式的值。中缀表达式转后缀表达式的思想是,从左到右遍历表达式中的每个数字和符号,遇到数字则输出,遇到符号,则要比较其与栈顶元素的优先级,若优先级低于栈顶元素,则将栈顶元素依次输出并将该元素入栈;若优先级高于栈顶元素则入栈;若是遇到左括号,入栈,入栈后的左括号优先级低于+ - * / ;若是遇到右括号,则将栈顶元素依次输出,直到遇到左括号。最后再将栈中元素依次输出,最终输出结果即为后缀表达式。
程序思想: 定义一个类ExpressionType ,有三个主要函数,一是将字符串中的数字和字符分割出来放到队列中,二是中缀变后缀函数,三是计算后缀表达式的值。其他函数有计算字符串长度,检测左括号和右括号是否匹配(只是为了保证原始字符串没有问题),重载操作符+(是为了把字符串赋给类的变量),两个构造函数。还有一个私有变量,m_string,既要计算的表达式字符串。
我用VS08新建一个空项目,添加一个ExpressionType.h文件,代码如下:
#include <string>
#include <stack>
#include <queue>
using namespace std;
class ExpressionType
{
public:
ExpressionType();
ExpressionType(string m_string);
void operator =(string m_string);
double Calculate();
private:
queue<string> DivideExpressionToItem();
stack<string> ChangeToStuff();
bool IsWellForm();
int Size();
private:
string m_string;
};
添加一个ExpressionType.cpp文件,代码如下:
#include <iostream>
#include "ExpressionType.h"
using namespace std;
ExpressionType::ExpressionType() {
m_string = "";
}
ExpressionType::ExpressionType(string m_string)
{
this->m_string = m_string;
}
void ExpressionType::operator =(string m_string)
{
this->m_string = m_string;
}
int ExpressionType::Size()
{
return m_string.size();
}
bool ExpressionType::IsWellForm()
{
stack<char> stack;
int size = Size();
char ch;
for ( int i=0;i<size;i++)
{
ch = m_string.at(i);
switch(ch)
{
case '(':
stack.push(ch);break;
case ')':
if (stack.empty())
{
return false;
}else
{
stack.pop();
}
break;
default:break;
}
}
return stack.empty();
}
queue<string> ExpressionType::DivideExpressionToItem()
{
queue<string> que;
if (! IsWellForm())
{
cout<<"error"<<endl;
return que;
}
string str ="";
char ch;
int size = Size();
bool isNumber = false;
for (int i =0;i<size;i++)
{
ch = m_string.at(i);
switch(ch)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case'.':
isNumber = true;break;
case'+':
case'-':
case '*':
case '/':
case '(':
case ')':
isNumber = false;break;
default:continue;
}
if (isNumber)
{
str += ch;
if (i == size -1)
{
que.push(str);
}
}
else
{
if (str.size() != 0)
{
que.push(str); //如果str之前保存了数据,则先入栈。
}
str = ch; // 如果第一个字符就是操作符号,直接入栈
que.push(str);
str = "";
}
}
return que;
}
/* 得到优先级,后面优先级高的返回true,否则返回false */
bool ExpressionType::GetProvity(string str1,string str2)
{
if (str1 == "+" || str1 == "-")
{
if (str2 == "(" || str2 == "*" || str2 == "/") //这些符号的优先级比+,-高。
{
return true;
}
else
return false;
}
else if (str1 == "*" || str1 == "/")
{
if (str2 == "(")
{
return true;
}
else
return false;
}
if (str1 == "(") //左括号之后的都要入栈,除了右括号,所以此时它的优先级最低
{
if (str2 == ")")
{
return false;
}
else
return true;
}
if (str1 == ")") //右括号,一般应该不存在这种情况
{
return true;
}
}
stack<string> ExpressionType::ChangeToStuff()
{
stack<string> stack_A;
stack<string> stack_B;
queue<string> que;
que = DivideExpressionToItem();
//cout<< "转换成队列的值"<<que<<endl;
if (que.empty())
{
return stack_B;
}
string str;
/*string str2 ;
bool probity;*/
while(!que.empty())
{
str = que.front();
que.pop();
if (str == "(")
{
stack_B.push(str);
}
else if (str == ")")
{
while(!stack_B.empty()&& stack_B.top() != "(")
{
stack_A.push( stack_B.top());
stack_B.pop();
}
if (!stack_B.empty())
{
stack_B.pop();
}
}
else if (str == "+" || str == "-")
{
if (stack_B.empty() || stack_B.top() == "(")
{
stack_B.push(str);
}
else
{
while(!stack_B.empty() && stack_B.top() != "(")
{
stack_A.push(stack_B.top());
stack_B.pop();
}
stack_B.push(str);
}
}
else if ( str == "*" || str == "/")
{
stack_B.push(str); //简单粗暴,不知道会不会出问题
}
else
{
stack_A.push(str); //数字直接入栈
}
}
//如果b栈中还有操作符,一次弹入a中
if (!stack_B.empty())
{
if (stack_B.top() != "(")
{
stack_A.push(stack_B.top());
stack_B.pop();
}
}
while(!stack_A.empty())
{
stack_B.push(stack_A.top());
cout<< stack_A.top()<<" ";
stack_A.pop();
}
return stack_B;
}
double ExpressionType::Calculate()
{
stack<string> stack_A = ChangeToStuff();
if (stack_A.empty())
return 0;
stack<double> stack;
string str;
char ch;
double db1;
while(!stack_A.empty())
{
str =stack_A.top();
stack_A.pop();
ch = str.at(0);
switch(ch)
{
case '+':
db1 = stack.top();
stack.pop();
db1 += stack.top();
stack.pop();
stack.push(db1);
break;
case '-':
db1 = stack.top();
stack.pop();
db1 = stack.top() -db1;
stack.pop();
stack.push(db1);
break;
case '*':
db1 = stack.top();
stack.pop();
db1 *= stack.top();
stack.pop();
stack.push(db1);
break;
case'/':
db1 = stack.top();
stack.pop();
if (db1 != 0.000)
{
db1 = stack.top()/db1;
stack.pop();
stack.push(db1);
}
break;
default:
//将字符串所代表的操作数转换成双精度浮点数并压入栈
char *p;
int len = str.length();
p= (char *)malloc((len+1)*sizeof(char));
str.copy(p,len,0);
stack.push(atof(p));
break;
}
}
return stack.top();
}
主函数ExpressionTypeMain.cpp文件,代码
#include <iostream>
#include "ExpressionType.h"
int main()
{
ExpressionType expr;
expr = "(2.99-4.32)*(90.8-78.66)+78.0/3.14"; //此时的“=”是重载之后的等号。
//expr = "9+3*2-10/2";
cout<<"result"<<expr.Calculate()<<endl;
cin.get();
return 0 ;
}
运行结果为:
JavaScript版: js版刚开始写的,输入即为字符串,没有考虑括号,现在懒得去改了,大家有好的建议,随时交流。主要有四个函数,确定符号优先级函数,中缀转后缀函数,根据操作符和操作数计算结果函数,计算后缀表达式函数。最后将结果输出到页面输入框,并弹出值。代码如下:
/*确定符号优先级函数,若当前元素优先级大于栈顶元素,返回true*/
var priority = function(char1,char2){
if (char1 == "+" || char1 == "-") {
if (char2 == "*" || char2 == "/" ){
return true;
}else{
return false;
}
}
if(char1 == "*" || char1 == "/"){
return false;
}
}
/*中缀表达式转为后缀表达式,返回存储后缀表达式的数组*/
var fontToBack = function(){
//var srcData = [9,+,(,3,-,1,),*,3,+,10,.,2];
// var srcData = srcStr.split("");
var srcData = [9,"+",3,"*",2,"-",10,"/",2];//+,3,*,2,-,2,*,4];
var i,len = srcData.length;
var desData = new Array();
var curData = new Array();
for(i=0;i<len;i++){
if (typeof(srcData[i]) == "number") {
desData.push(srcData[i]);
}else{
if (curData.length == 0) {
curData.push(srcData[i]);
}else{
for(var j = curData.length -1;j>=0; j--){
var previous = curData[curData.length -1];
var key = priority(previous,srcData[i]);
if (key) {
curData.push(srcData[i]);
break;
}if(!key){
desData.push(previous);
curData.pop();
if (curData.length == 0) {
curData.push(srcData[i]);
}
}
}
}
}
}
while( curData.length>0){
desData.push(curData.pop());
}
//alert(desData);
return desData;
}
/*根据操作符和操作数计算结果 */
var count = function(operator,num1,num2){
// var result;
if (operator == "+") {
return (num1 + num2);
}if (operator == "-") {
return num1 - num2;
}if (operator == "*") {
return num1*num2;
}if(operator == "/"){}
return num1 / num2;
}
/*
主函数 ,返回表达式结果
*/
var countResult = function(arr){
var reArr = new Array();
var num1 ,num2,ret;
arr.forEach(function(item,index,arr){
if (typeof(item)=="number") {
reArr.push(item);
}else{
num2 = reArr.pop();
num1 = reArr.pop();
ret =count(item,num1,num2);
reArr.push(ret);
}
})
// alert(ret);
return ret;
}
// var srcStr = document.getElementById("inData").value;
var arr = fontToBack();
//alert(arr);
document.getElementById("outData").value = countResult(arr);
alert(countResult(arr));
我输入的数组是: [9,"+",3,"*",2,"-",10,"/",2];
chrome浏览器下结果页面:
还有很对不足之处,欢迎交流。
点击打开链接 。
首先,明确两个个概念,“中缀表达式”和 ”后缀表达式“一般的运算表达式。例如”9+(3-1)*3+10/2“,这是中缀表达式,而”9 3 1- 3 *+10 2 / +“则是后缀表达式,计算后缀表达式的值,先把数字入栈,遇到操作符则栈顶两个数字出栈运算,然后再把结果存入栈中,比如,计算该后缀表达式的过程为
,①9,3,1入栈,遇到‘-’,计算3-1=2,将2入栈,此时栈中为9,2。②接着3入栈,栈中为9,3,2,然后遇到‘*’,栈顶两个数字3,2出栈计算3*2=6,将6入栈,此时栈中为9,6。③接着遇到‘+’号,栈顶两元素出栈,计算9+6=15,将15入栈,此时栈中为15。④,然后10,2,入栈,最后遇到‘/’,栈顶两数字10,2出栈计算,10/2 =5,并将5入栈,此时栈中为15,5。⑤遇到操作符‘+’,将栈顶两元素弹 出,计算15+5 = 20.入栈再出栈,即为最后结果。
所以计算表达式的值有两个关键步骤,一是将中缀表达式转为后缀表达式;二是计算后缀表达式的值。中缀表达式转后缀表达式的思想是,从左到右遍历表达式中的每个数字和符号,遇到数字则输出,遇到符号,则要比较其与栈顶元素的优先级,若优先级低于栈顶元素,则将栈顶元素依次输出并将该元素入栈;若优先级高于栈顶元素则入栈;若是遇到左括号,入栈,入栈后的左括号优先级低于+ - * / ;若是遇到右括号,则将栈顶元素依次输出,直到遇到左括号。最后再将栈中元素依次输出,最终输出结果即为后缀表达式。
程序思想: 定义一个类ExpressionType ,有三个主要函数,一是将字符串中的数字和字符分割出来放到队列中,二是中缀变后缀函数,三是计算后缀表达式的值。其他函数有计算字符串长度,检测左括号和右括号是否匹配(只是为了保证原始字符串没有问题),重载操作符+(是为了把字符串赋给类的变量),两个构造函数。还有一个私有变量,m_string,既要计算的表达式字符串。
我用VS08新建一个空项目,添加一个ExpressionType.h文件,代码如下:
#include <string>
#include <stack>
#include <queue>
using namespace std;
class ExpressionType
{
public:
ExpressionType();
ExpressionType(string m_string);
void operator =(string m_string);
double Calculate();
private:
queue<string> DivideExpressionToItem();
stack<string> ChangeToStuff();
bool IsWellForm();
int Size();
private:
string m_string;
};
添加一个ExpressionType.cpp文件,代码如下:
#include <iostream>
#include "ExpressionType.h"
using namespace std;
ExpressionType::ExpressionType() {
m_string = "";
}
ExpressionType::ExpressionType(string m_string)
{
this->m_string = m_string;
}
void ExpressionType::operator =(string m_string)
{
this->m_string = m_string;
}
int ExpressionType::Size()
{
return m_string.size();
}
bool ExpressionType::IsWellForm()
{
stack<char> stack;
int size = Size();
char ch;
for ( int i=0;i<size;i++)
{
ch = m_string.at(i);
switch(ch)
{
case '(':
stack.push(ch);break;
case ')':
if (stack.empty())
{
return false;
}else
{
stack.pop();
}
break;
default:break;
}
}
return stack.empty();
}
queue<string> ExpressionType::DivideExpressionToItem()
{
queue<string> que;
if (! IsWellForm())
{
cout<<"error"<<endl;
return que;
}
string str ="";
char ch;
int size = Size();
bool isNumber = false;
for (int i =0;i<size;i++)
{
ch = m_string.at(i);
switch(ch)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case'.':
isNumber = true;break;
case'+':
case'-':
case '*':
case '/':
case '(':
case ')':
isNumber = false;break;
default:continue;
}
if (isNumber)
{
str += ch;
if (i == size -1)
{
que.push(str);
}
}
else
{
if (str.size() != 0)
{
que.push(str); //如果str之前保存了数据,则先入栈。
}
str = ch; // 如果第一个字符就是操作符号,直接入栈
que.push(str);
str = "";
}
}
return que;
}
/* 得到优先级,后面优先级高的返回true,否则返回false */
bool ExpressionType::GetProvity(string str1,string str2)
{
if (str1 == "+" || str1 == "-")
{
if (str2 == "(" || str2 == "*" || str2 == "/") //这些符号的优先级比+,-高。
{
return true;
}
else
return false;
}
else if (str1 == "*" || str1 == "/")
{
if (str2 == "(")
{
return true;
}
else
return false;
}
if (str1 == "(") //左括号之后的都要入栈,除了右括号,所以此时它的优先级最低
{
if (str2 == ")")
{
return false;
}
else
return true;
}
if (str1 == ")") //右括号,一般应该不存在这种情况
{
return true;
}
}
stack<string> ExpressionType::ChangeToStuff()
{
stack<string> stack_A;
stack<string> stack_B;
queue<string> que;
que = DivideExpressionToItem();
//cout<< "转换成队列的值"<<que<<endl;
if (que.empty())
{
return stack_B;
}
string str;
/*string str2 ;
bool probity;*/
while(!que.empty())
{
str = que.front();
que.pop();
if (str == "(")
{
stack_B.push(str);
}
else if (str == ")")
{
while(!stack_B.empty()&& stack_B.top() != "(")
{
stack_A.push( stack_B.top());
stack_B.pop();
}
if (!stack_B.empty())
{
stack_B.pop();
}
}
else if (str == "+" || str == "-")
{
if (stack_B.empty() || stack_B.top() == "(")
{
stack_B.push(str);
}
else
{
while(!stack_B.empty() && stack_B.top() != "(")
{
stack_A.push(stack_B.top());
stack_B.pop();
}
stack_B.push(str);
}
}
else if ( str == "*" || str == "/")
{
stack_B.push(str); //简单粗暴,不知道会不会出问题
}
else
{
stack_A.push(str); //数字直接入栈
}
}
//如果b栈中还有操作符,一次弹入a中
if (!stack_B.empty())
{
if (stack_B.top() != "(")
{
stack_A.push(stack_B.top());
stack_B.pop();
}
}
while(!stack_A.empty())
{
stack_B.push(stack_A.top());
cout<< stack_A.top()<<" ";
stack_A.pop();
}
return stack_B;
}
double ExpressionType::Calculate()
{
stack<string> stack_A = ChangeToStuff();
if (stack_A.empty())
return 0;
stack<double> stack;
string str;
char ch;
double db1;
while(!stack_A.empty())
{
str =stack_A.top();
stack_A.pop();
ch = str.at(0);
switch(ch)
{
case '+':
db1 = stack.top();
stack.pop();
db1 += stack.top();
stack.pop();
stack.push(db1);
break;
case '-':
db1 = stack.top();
stack.pop();
db1 = stack.top() -db1;
stack.pop();
stack.push(db1);
break;
case '*':
db1 = stack.top();
stack.pop();
db1 *= stack.top();
stack.pop();
stack.push(db1);
break;
case'/':
db1 = stack.top();
stack.pop();
if (db1 != 0.000)
{
db1 = stack.top()/db1;
stack.pop();
stack.push(db1);
}
break;
default:
//将字符串所代表的操作数转换成双精度浮点数并压入栈
char *p;
int len = str.length();
p= (char *)malloc((len+1)*sizeof(char));
str.copy(p,len,0);
stack.push(atof(p));
break;
}
}
return stack.top();
}
主函数ExpressionTypeMain.cpp文件,代码
#include <iostream>
#include "ExpressionType.h"
int main()
{
ExpressionType expr;
expr = "(2.99-4.32)*(90.8-78.66)+78.0/3.14"; //此时的“=”是重载之后的等号。
//expr = "9+3*2-10/2";
cout<<"result"<<expr.Calculate()<<endl;
cin.get();
return 0 ;
}
运行结果为:
JavaScript版: js版刚开始写的,输入即为字符串,没有考虑括号,现在懒得去改了,大家有好的建议,随时交流。主要有四个函数,确定符号优先级函数,中缀转后缀函数,根据操作符和操作数计算结果函数,计算后缀表达式函数。最后将结果输出到页面输入框,并弹出值。代码如下:
/*确定符号优先级函数,若当前元素优先级大于栈顶元素,返回true*/
var priority = function(char1,char2){
if (char1 == "+" || char1 == "-") {
if (char2 == "*" || char2 == "/" ){
return true;
}else{
return false;
}
}
if(char1 == "*" || char1 == "/"){
return false;
}
}
/*中缀表达式转为后缀表达式,返回存储后缀表达式的数组*/
var fontToBack = function(){
//var srcData = [9,+,(,3,-,1,),*,3,+,10,.,2];
// var srcData = srcStr.split("");
var srcData = [9,"+",3,"*",2,"-",10,"/",2];//+,3,*,2,-,2,*,4];
var i,len = srcData.length;
var desData = new Array();
var curData = new Array();
for(i=0;i<len;i++){
if (typeof(srcData[i]) == "number") {
desData.push(srcData[i]);
}else{
if (curData.length == 0) {
curData.push(srcData[i]);
}else{
for(var j = curData.length -1;j>=0; j--){
var previous = curData[curData.length -1];
var key = priority(previous,srcData[i]);
if (key) {
curData.push(srcData[i]);
break;
}if(!key){
desData.push(previous);
curData.pop();
if (curData.length == 0) {
curData.push(srcData[i]);
}
}
}
}
}
}
while( curData.length>0){
desData.push(curData.pop());
}
//alert(desData);
return desData;
}
/*根据操作符和操作数计算结果 */
var count = function(operator,num1,num2){
// var result;
if (operator == "+") {
return (num1 + num2);
}if (operator == "-") {
return num1 - num2;
}if (operator == "*") {
return num1*num2;
}if(operator == "/"){}
return num1 / num2;
}
/*
主函数 ,返回表达式结果
*/
var countResult = function(arr){
var reArr = new Array();
var num1 ,num2,ret;
arr.forEach(function(item,index,arr){
if (typeof(item)=="number") {
reArr.push(item);
}else{
num2 = reArr.pop();
num1 = reArr.pop();
ret =count(item,num1,num2);
reArr.push(ret);
}
})
// alert(ret);
return ret;
}
// var srcStr = document.getElementById("inData").value;
var arr = fontToBack();
//alert(arr);
document.getElementById("outData").value = countResult(arr);
alert(countResult(arr));
我输入的数组是: [9,"+",3,"*",2,"-",10,"/",2];
chrome浏览器下结果页面:
还有很对不足之处,欢迎交流。
相关文章推荐
- 初识io.js
- jsp引用C标签报错小常识
- Sublime Text插件:HTML+CSS+JAVASCRIPT+JSON快速格式化
- js数组最大值max和最小值min
- JSP+Servlet使用commons-fileupload上传文件连接被重置解决方案
- Underscore.js常用方法总结
- javascript常用数组算法总结
- jsapi_ticket更新-accesstoken更新
- ES6-Symbol类型
- extjs学习——sprite.Text获取数据并绘图
- js实现a标签超链接提交form表单的方法
- JavaScript 学习笔记
- 使用trigger解耦
- setTimeout(fn, 15)这样的东西到底有没有意义?
- ie中自动识别单屏与双屏(js)
- JS倒计时代码汇总
- ExtJs4.1打开新窗口
- js array 常用方法
- 返回顶部JavaScript插件
- JavaScript Constructors