zoukankan      html  css  js  c++  java
  • 计算器核心算法——中缀表达式转为后缀表达式

    中缀表达式转后缀表达式的过程类似编译过程
    ——四则运算表达式中的括号必须匹配
    ——根据运算符优先级进行转换
    ——转换后的表达式中没有括号
    ——转换后可以顺序的计算出最终结果

    这是某位伟人研究出的算法,在这里我们直接拿来用就可以。

    转换过程:
    ——当前元素e为数字:输出
    ——当前元素e为运算符:
    1.与栈顶运算符进行优先级比较
    2.小于等于:将栈顶元素输出,转1
    3.大于:将当前元素e入栈

    ——当前元素e为左括号:入栈
    ——当前元素e为右括号:
    1.弹出栈顶元素并输出,直至栈顶元素为左括号
    2.将栈顶的左括号从栈中弹出

    while( !exp.isEmpty() )
    {
        QString s = exp.dequeue();
        
        if(isNumber(e))
            输出e;
        else if(isOperator(e))
        {
            while( priority(e) <= priority(stack.top() ))
                输出栈顶元素: stack.pop();
                
            stack.push(e);
        }
        else if(isLeft(e))
            stack.push(e);
        else if(isRight(e))
        {
            while( !isLeft(stack.top() ))
                输出栈顶元素 stack.pop();
                
            从栈中弹出左括号: stack.pop();
        }
    }

    exp是上篇博客中用分离算法得到的队列了,将里面的每个元算都处理,也就是说一直处理到这个队列为空为止。

    关键点:转换过程中左右括号是重要标志
    ——如何确保表达式中的括号能够左右匹配?

    匹配算法实现:

    合法的四则运算表达式
    ——括号匹配成对出现
    ——左括号必然先于右括号出现

    for(int i=0; i<len; i++)
    {
        if(exp[i]为左括号)
            exp[i]入栈;
        else if(exp[i]为右括号)
        {
            if(栈顶元素为左括号)
                将栈顶元素弹出;
            else    
                匹配错误
        }
    }

     QCalculatorDec.cpp

    #include "QCalculatorDec.h"
    #include <QDebug>
    
    QCalculatorDec::QCalculatorDec()
    {
        m_exp = " ";
        m_result = " ";
    //为了测试使用
        QQueue<QString> r = split("-9.11+ (3 - -1)* -5");
    
        for(int i=0; i<r.length(); i++)
        {
            qDebug() << r[i];
        }
    
        qDebug() << match(r);
    }
    
    QCalculatorDec::~QCalculatorDec()
    {
    
    }
    
    bool QCalculatorDec::isDigitOrDot(QChar c)
    {
        return ((('0' <= c) && (c <= '9')) || (c == '.'));
    }
    
    bool QCalculatorDec::isSymbol(QChar c)   //判读当前的字符C究竟是不是操作符或者括号
    {
        return isOperator(c) || (c == '(') || (c == ')');
    }
    
    bool QCalculatorDec::isSign(QChar c)  //判断当前的字符是不是正负号
    {
        return (c == '+') || (c == '-');
    }
    
    bool QCalculatorDec::isNumber(QString s)  //判断当前的s是不是合法的数字
    {
        bool ret = false;
    
        s.toDouble(&ret);
    
        return ret;
    }
    
    bool QCalculatorDec::isOperator(QString s)
    {
        return (s == "+") || (s == "-") || (s == "*") || (s == "/") ;
    }
    
    bool QCalculatorDec::isLeft(QString s)
    {
        return (s == "(");
    }
    
    bool QCalculatorDec::isRight(QString s)
    {
        return (s == ")");
    }
    
    int QCalculatorDec::priority(QString s)
    {
        int ret = 0;
    
        if((s == "+") || (s == "-"))
        {
            ret = 1;
        }
    
        if((s == "*") || (s == "/"))
        {
            ret = 2;
        }
    
        return ret;
    }
    
    bool QCalculatorDec::expression(const QString &exp)
    {
        bool ret = false;
    
        return ret;
    }
    
    QString QCalculatorDec::result()
    {
        return m_result;
    }
    
    QQueue<QString> QCalculatorDec::split(const QString &exp)
    {
        QQueue<QString> ret;
        QString num = "";
        QString pre = ""; //用来保存前一个字符的
    
        for(int i=0; i<exp.length(); i++)
        {
            if(isDigitOrDot(exp[i]))
            {
                num += exp[i];
                pre = exp[i];
            }
            else if(isSymbol(exp[i]))
            {
                if(!num.isEmpty())
                {
                    ret.enqueue(num);   //如果不为空,就应该分离并保存了。保存到队列中,之后num就应该清空,以便累计下一个运算数。
    
                    num.clear();
                }
    
                if(isSign(exp[i]) && ((pre == "") || (pre == "(") || isOperator(pre)))
                {
                    num += exp[i];
                }
                else
                {
                    ret.enqueue(exp[i]);
                }
    
                pre = exp[i];//将这个字符保存下来,当进行下一次循环时,它将作为前一个字符使用
            }
        }
    
        if(!num.isEmpty())  //如果for循环运行结束之后,num变量里面还有没有东西呢?如果不为空,里面还保存着最后的一个运算数。应将其分离保存到返回队列中去。
        {
            ret.enqueue(num);
        }
    
        return ret;
    }
    bool QCalculatorDec::match(QQueue<QString> exp)
    {
        bool ret = true;
        int len = exp.length();
        QStack<QString> stack;
    
        for(int i=0; i<len; i++)
        {
            if(isLeft(exp[i]))
            {
                 stack.push(exp[i]);
            }
            else if(isRight(exp[i]))
            {
                if( !stack.isEmpty() && isLeft(stack.top()))
                {
                    stack.pop();  //遇到一个右括号就会将左括号弹出栈。
                }
                else
                {
                    ret = false;
                    break;
                }
            }
        }
    
        if( !stack.isEmpty()) //因为在上面的程序中遇到一个右括号就会将左括号弹出栈,如果左右括号完全匹配的话,最后栈中是没有括号的,即为空。
        {                     //就是为了处理"-9.11+ (3 - (-1)* -5" 左括号比右括号多的问题。
            ret = false;
        }
    
        return ret;
    }

     中缀转后缀算法:

    #include "QCalculatorDec.h"
    #include <QDebug>
    
    QCalculatorDec::QCalculatorDec()
    {
        m_exp = " ";
        m_result = " ";
    //为了测试使用
        QQueue<QString> r = split("-9.11+ (3 - -1)* -5");
    
        for(int i=0; i<r.length(); i++)
        {
            qDebug() << r[i];
        }
    
        QQueue<QString> output;
    
        transform(r,output);
    
        for(int i=0; i<output.length(); i++)
        {
            qDebug() << output[i];
        }
    
    }
    
    QCalculatorDec::~QCalculatorDec()
    {
    
    }
    
    bool QCalculatorDec::isDigitOrDot(QChar c)
    {
        return ((('0' <= c) && (c <= '9')) || (c == '.'));
    }
    
    bool QCalculatorDec::isSymbol(QChar c)   //判读当前的字符C究竟是不是操作符或者括号
    {
        return isOperator(c) || (c == '(') || (c == ')');
    }
    
    bool QCalculatorDec::isSign(QChar c)  //判断当前的字符是不是正负号
    {
        return (c == '+') || (c == '-');
    }
    
    bool QCalculatorDec::isNumber(QString s)  //判断当前的s是不是合法的数字
    {
        bool ret = false;
    
        s.toDouble(&ret);
    
        return ret;
    }
    
    bool QCalculatorDec::isOperator(QString s)
    {
        return (s == "+") || (s == "-") || (s == "*") || (s == "/") ;
    }
    
    bool QCalculatorDec::isLeft(QString s)
    {
        return (s == "(");
    }
    
    bool QCalculatorDec::isRight(QString s)
    {
        return (s == ")");
    }
    
    int QCalculatorDec::priority(QString s)
    {
        int ret = 0;
    
        if((s == "+") || (s == "-"))
        {
            ret = 1;
        }
    
        if((s == "*") || (s == "/"))
        {
            ret = 2;
        }
    
        return ret;
    }
    
    bool QCalculatorDec::expression(const QString &exp)
    {
        bool ret = false;
    
        return ret;
    }
    
    QString QCalculatorDec::result()
    {
        return m_result;
    }
    
    QQueue<QString> QCalculatorDec::split(const QString &exp)
    {
        QQueue<QString> ret;
        QString num = "";
        QString pre = ""; //用来保存前一个字符的
    
        for(int i=0; i<exp.length(); i++)
        {
            if(isDigitOrDot(exp[i]))
            {
                num += exp[i];
                pre = exp[i];
            }
            else if(isSymbol(exp[i]))
            {
                if(!num.isEmpty())
                {
                    ret.enqueue(num);   //如果不为空,就应该分离并保存了。保存到队列中,之后num就应该清空,以便累计下一个运算数。
    
                    num.clear();
                }
    
                if(isSign(exp[i]) && ((pre == "") || (pre == "(") || isOperator(pre)))
                {
                    num += exp[i];
                }
                else
                {
                    ret.enqueue(exp[i]);
                }
    
                pre = exp[i];//将这个字符保存下来,当进行下一次循环时,它将作为前一个字符使用
            }
        }
    
        if(!num.isEmpty())  //如果for循环运行结束之后,num变量里面还有没有东西呢?如果不为空,里面还保存着最后的一个运算数。应将其分离保存到返回队列中去。
        {
            ret.enqueue(num);
        }
    
        return ret;
    }
    bool QCalculatorDec::match(QQueue<QString> exp)
    {
        bool ret = true;
        int len = exp.length();
        QStack<QString> stack;
    
        for(int i=0; i<len; i++)
        {
            if(isLeft(exp[i]))
            {
                 stack.push(exp[i]);
            }
            else if(isRight(exp[i]))
            {
                if( !stack.isEmpty() && isLeft(stack.top()))
                {
                    stack.pop();  //遇到一个右括号就会将左括号弹出栈。
                }
                else
                {
                    ret = false;
                    break;
                }
            }
        }
    
        if( !stack.isEmpty()) //因为在上面的程序中遇到一个右括号就会将左括号弹出栈,如果左右括号完全匹配的话,最后栈中是没有括号的,即为空。
        {                     //就是为了处理"-9.11+ (3 - (-1)* -5" 左括号比右括号多的问题。
            ret = false;
        }
    
        return ret;
    }
    bool QCalculatorDec::transform(QQueue<QString> &exp, QQueue<QString> &output)
    {
        bool ret = match(exp); //在中缀转后缀表达式之前,首先要看一下括号是否匹配。
        QStack<QString> stack;
        output.clear();
    
        while(ret &&  !exp.isEmpty() )
        {
            QString e = exp.dequeue();
    
            if( isNumber(e) )
            {
                output.enqueue(e);//当前的元素是数字,直接保存,放到输出队列中
            }
    
            else if( isOperator(e) )
            {
                while( !stack.isEmpty()  && priority(e)<= priority(stack.top()) )//如果当前元素的优先级小于栈顶元素的优先级,那么输出栈顶元素。
                {
                    output.enqueue(stack.top());
                }
    
                stack.push(e);
            }
    
    
            else if( isLeft(e) )
            {
                stack.push(e);
            }
    
            else if( isRight(e) )
            {
                while( !stack.isEmpty() && !isLeft(stack.top()) ) //如果栈顶元素不是左括号,输出保存
                {
                    output.enqueue(stack.pop() );
                }
    
                if( !stack.isEmpty() )
                {
                    stack.pop();  //将栈顶的左括号弹出去不要了。
                }
            }
            else
            {
                ret = false;
            }
        }
    
        while( !stack.isEmpty())  //中缀转后缀的操作,将括号不要了。但是其他的一个都不能缺少。因此遍历栈中的元素。
        {
            output.enqueue(stack.pop());
        }
    
        if(!ret)  //转换失败,将输出队列清空。
        {
            output.clear();
        }
    
        return ret;
    }
  • 相关阅读:
    JEECG弹出框提交表单
    JEECG开发第一个菜单显示设备列表
    优化jeecg底层高级查询,支持bool值和下拉框查询
    Jeecg中的<t:datagrid treegrid="true">实现
    JEECG 简单实例讲解权限控制
    jeecg 定时任务配置用法
    帆软报表和jeecg的进一步整合--ajax给后台传递map类型的参数
    JEECG与帆软报表集成
    helm部署Redis哨兵集群
    helm安装kafka集群并测试其高可用性
  • 原文地址:https://www.cnblogs.com/-glb/p/12104939.html
Copyright © 2011-2022 走看看