QT简易计算器--表达式计算核心算法(二)

来源:互联网 发布:java 查看线程状态 编辑:程序博客网 时间:2024/05/16 15:08

概述:上篇我主要介绍了用QT做计算器的整个流程,这次主要就是分析一下,计算器表达式计算的算法部分。因为也找了很多别人写的代码,但大多都是只支持个位数的加减乘除,小数也不支持,所以就在原有框架上,修改,优化,让其满足我想要的功能。
1,表达式计算思路。
表达式数据操作符分割–>转为逆波兰表达式–>计算逆波兰表达式值–>输出结果。
2,表达式计算详解。
(1)表达式数据操作符分割
首先我们得到了一个字符串表达式,我们将数据和操作符分割。
如:表达式—————> “23+1.23-(2-5)*3”
分割后为“23” “+” “1.23” “- ”“( ”“2”“ -” “5” “)”“*” “3”,然后将其存入一个QString数组,这样数据,操作符分割就便于后面计算,辨别是数据还是操作符了。遍历数组,只要第一个字符不是0-9就是操作符了。
下面是这部分代码详解:

/*将表达式的数据,操作符分割,依次存入mask_buffer数组中*/int Calculator::mask_data(QString expression, QString *mask_buffer){    int i,k = 0,cnt = 0;    QString::iterator p = expression.begin();//获取表达式头指针    int length = expression.length();//获取表达式数据长度    /*for 循环遍历整个表达式,进行数据,操作符分割*/    for(i = 0 ; i < length ; i += cnt,k++)    {        cnt = 0;        if(*p >= '0' && *p <= '9')        {            /*当第一个字符为0-9时,肯定是数字*/            QString temp = *p;            p ++;            cnt ++;            /*看数字后面是否还是数字或小数点,直到遇到下个操作符,这个数据才结束*/            while((*p >= '0' && *p <= '9') || *p == '.')            {                temp += *p;                p++;                cnt ++;            }            mask_buffer[k] = temp;        }else{        /*不是数字,就是操作符,直接写入数组即可*/            QString temp = *p;            p++;            cnt ++;            mask_buffer[k] = temp;        }    }    return k;}

(2)转为逆波兰表达式。
既然用到逆波兰表达式,首先了解一下什么是逆波兰表达式。我们正常的表达式是中缀表达式,逆波兰就是将中缀表达式转化为后缀表达式。我们就简单粗暴一些,例子。
1+2*3-(2+5)———>中缀表达式。
1,2,3,*,+,2,5,+,- ———>后缀表达式。
中缀到后缀如何转化呢,下面就是详细的规则了:
首先我们定义一个数组A,堆栈B。数组A用于存储最后的后缀表达式,堆栈B就是一个过渡器,帮助我们转化,再定义操作符的优先级,如+ - * / ( )。
从左到右遍历整个表达式。
1,遇到数字加入数组A。
2,遇到左括号直接入栈到B。
3,遇到右括号,堆栈B出栈,加入到数组A中,直到遇到左括号,将左括号出栈但不加入数组A中。
4,如果遇到的是运算符:
(1)当堆栈B为空,直接入栈到B。
(2)当堆栈不为空,当前运算符优先级大于堆顶运算符优先级,入栈到B。
(3)当堆栈不为空,当前运算符优先级小于等于堆顶运算符优先级,B出栈到A数组中,直到堆顶运算符优先级小于当前运算符优先级,再将当前运算符入栈B。
5,当遍历完整个表达式,堆栈B不为空,依次出栈加入到A数组中。

下面详细步骤转化过程:

数组A[100],堆栈B.表达式:1+2*3-(2+5)遍历:"1"   ------>        A{"1"}  B{空}"+"   ------>        A{"1"}  B{"+"}"2"   ------>        A{"1","2"}  B{"+"}"*"   ------>        A{"1","2"}  B{"+","*"}         //"*"优先级高于"+""3"   ------>        A{"1","2","3"}  B{"+","*"}"-"   ------>        A{"1","2","3","*","+"}  B{"-"} //"-"优先级不大于"*",不大于"+",*,+出栈到A"("   ------>        A{"1","2","3","*","+"}  B{"-","("}   "2"   ------>        A{"1","2","3","*","+","2"}  B{"-","("}"+"   ------>        A{"1","2","3","*","+","2"}  B{"-","(","+"}"5"   ------>        A{"1","2","3","*","+","2","5"}  B{"-","(","+"}")"   ------>        A{"1","2","3","*","+","2","5","+"}  B{"-"}     //遇到右括号,+出栈到AA{"1","2","3","*","+","2","5","+""-"}  B{空}   //遍历结束,B中不为空,"-"出栈到A

下面是这部分源码:

/*获取操作符优先级*/int Calculator::Priority(QString data){    int priority;    if(data == "(")        priority = 1;    else if(data == "+" || data == "-")        priority = 2;    else if(data == "*" || data == "/")        priority = 3;    else if (data == ")")        priority = 4;    else        priority = -1;    return priority;}/*将获取到的分割好的表达式数组,转化为逆波兰表达式,存入数组repolish中*/int Calculator::re_polish(QString *mask_buffer,QString *repolish,int length){    QStack<QString> st2;    int i = 0;    for(int j = 0 ; j < length ; j++)    {        if(mask_buffer[j] != "(" && mask_buffer[j] != ")" && mask_buffer[j] != "+" && mask_buffer[j] != "-" && mask_buffer[j] != "*" && mask_buffer[j] != "/" )            repolish[i++] = mask_buffer[j];        else if(mask_buffer[j] == "("){            st2.push(mask_buffer[j]);        }        else if(mask_buffer[j] == ")"){            while(st2.top() != "(")            {                repolish[i++] = st2.top();                st2.pop();            }            if(st2.top() == "(")                st2.pop();        }        else if(st2.empty() || Priority(mask_buffer[j]) > Priority(st2.top()))            st2.push(mask_buffer[j]);        else{            while(Priority(mask_buffer[j]) <= Priority(st2.top()))            {                repolish[i++] = st2.top();                st2.pop();                if(st2.empty())                    break;            }            st2.push(mask_buffer[j]);        }    }    while(!st2.empty())    {        repolish[i++] = st2.top();        st2.pop();    }    return i;}

(3)计算逆波兰表达式值。
计算就比较简单了,我们只需要维护一个保存数据的堆栈就行了,我们有一个逆波兰表达式数组,上面得到的,再新建一个堆栈st。遍历整个逆波兰表达式数组,遇到数字直接压栈到st中,遇到“+”,“-”,“*”,“/”,就取出st栈顶元素a,st.pop(),取下一个栈顶元素b,st.pop()。计算数值对应操作符值(如操作符为+,b+a),将计算的值入栈到st中。直到表达式遍历完,堆栈里只剩一个数据,就是最后的结果值,取出。
我们还是以 1,2,3,*,+,2,5,+,- 为例。

遍历数组A[] = {"1","2","3","*","+","2","5","+""-"} "1"  ------>    st{1} "2"  ------>    st{1,2} "3"  ------>    st{1,2,3} "*"  ------>    st{1,6} //2*3 = 6 "+"  ------>    st{7}   //1+6 = 7 "2"  ------>    st{7,2} "5"  ------>    st{7,2,5} "+"  ------>    st{7,7}  //2+5 = 7 "-"  ------>    st{0}    //7-7 = 0   即最后结果为0

下面是这部分源码:

/*计算逆波兰表达式值并显示*/double Calculator::repolish_calculat(QString *repolish,int length){    QStack <double> st;    for(int m = 0 ; m < length ; m ++)    {        if(repolish[m] != "+" && repolish[m] != "-" && repolish[m] != "*" && repolish[m] != "/" )        {            /*Qstring转化为double数据存入堆栈*/            st.push(repolish[m].toDouble());        }        else        {            if(repolish[m] == "+")            {                double a = st.top();                st.pop();                double b = st.top();                st.pop();                st.push(b + a);            }            else if(repolish[m] == "-")            {                double a = st.top();                st.pop();                double b = st.top();                st.pop();                st.push(b - a);            }            else if(repolish[m] == "*")            {                double a = st.top();                st.pop();                double b = st.top();                st.pop();                st.push(b * a);            }            else if(repolish[m] == "/")            {                double a = st.top();                st.pop();                double b = st.top();                st.pop();                if(a != 0)                   st.push(b/a);                else                {                    ui->display->clear();                    ui->put_data->setText("0 不能做除数");                    return -1;                }            }        }    }    QString res = QString::number(st.top(),'g',10);    ui->display->clear();    ui->put_data->setText(res);    return st.top();}

3,项目完整源码在上篇中有链接,需要的话可直接下载。

原创粉丝点击