首页 > 【数据结构】支持四则混合运算的计算器(转)

【数据结构】支持四则混合运算的计算器(转)

  1.给出两个数,用户再指定操作符,要求计算结果,这实现起来很容易;

    2.多个数,但只涉及同一优先级的操作符,做起来也很容易;

    3.多个数,不同优先级的操作符,怎么办呢?

   想想就头痛,不过还好前人已经为我们留下了很多解决这个问题的方法。通过逆波兰表达式是解决这个问题很流行的一种方式。

 

   一、什么是逆波兰表达式?

    我们一般使用的表达式,形如1+2*3,被称为中缀表达式,转换为后缀表达式即为:1 2 3 * +,而后缀表达式也就是我们所说的逆波兰表达式。

    逆波兰表达式计算起来非常方便:遇操作数入栈,遇操作符则弹出栈顶两个元素进行计算并将结果推入栈中,直至结束。上面的表达式的计算过程可以用下图表示:

 

    也正因为逆波兰的方便性,使它成为了计算器的普遍实现方式。

   二、四则混合运算计算器

    既然思路已经清晰了,那么我们的计算器可以分两步走:

    1.将表达式转换为逆波兰形式

    2.逆波兰表达式求值。

    

     生成逆波兰表达式:

    将一般中缀表达式转换为逆波兰表达式有如下转换过程:

    (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。

    (2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优 先级最低的特殊符号“#”。

    (3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串 的结束并将该数字串直接输出。

    (4)如果不是数字,该字符则是运算符,此时需比较优先关系。

    做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符 栈顶的运算符,则将该运算符入栈。倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。

    (5)重复上述操作(3)-(4)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。

            而上面提到的运算符优先级如下:

操作符#^*,/,%+,- ()
isp(栈内优先级)075318
icp(栈外优先级)064281

    下面是程序化算法流程:

    1、建立运算符栈stackOperator用于运算符的存储,压入''。

    2、预处理表达式,正、负号前加0(如果一个加号(减号)出现在最前面或左括号后面,则该加号 (减号)为正负号) 。

    3、顺序扫描表达式,如果当前字符是数字(优先级为0的符号),则直接输出该数字;如果当前字 符为运算符或括号(优先级不为0的符号),则判断第4点 。

    4、若当前运算符为'(',直接入栈;

    若为')',出栈并顺序输出运算符直到遇到第一个'(',遇到的第一个'('出栈但不输出;

    若为其它,比较stackOperator栈顶元素与当前元素的优先级:

    如果 栈顶元素 >= 当前元素,出栈并顺序输出运算符直到 栈顶元素 < 当前元素,然后当前元素入栈;

    如果 栈顶元素 < 当前元素,直接入栈。

    5、重复第3点直到表达式扫描完毕。

    6、顺序出栈并输出运算符直到栈顶元素为''。 

 

    a.预处理表达式,对于一元+,-在前面加0:

///

        ///处理正负号

         ///


        ///

        ///

privatestaticstringFormatExp(stringexp)

        {

            var result =exp.Trim().Replace("", "");



            if(result[0] =='+'||result[0] =='-')

            {

                result ="0"+result;

            }

            for(var i =0; i 
            {

                if(result[i] =='('&&(result[i +1] =='+'||result[i +1] =='-'))

                {

                    result =result.Substring(0, i +1) +"0"+result.Substring(i +1);

                }

            }



            returnresult;

        }

 

    b.将格式化后的表达式转换为逆波兰表达式

///

        ///将解析过后的表达式转换为逆波兰表达式(各个语义单元存放在列表中)

        ///


        ///

        ///

privatestaticListToRPNExp(Listtokens)

        {

            var result =newList();

            var opStack =newStack();





            opStack.Push("");



            for(var i =0; i 
            {

                if(IsOperator(tokens[i]))

                    result.AddRange(HandleOperator(opStack, tokens[i]));

                elseresult.Add(tokens[i]);

            }



            while(opStack.Peek() !="")

            {

                result.Add(opStack.Pop());

            }

            returnresult;

        }

        ///

        ///根据操作符的优先级决定入栈还是出栈

        ///


        ///

        ///

        ///

privatestaticListHandleOperator(Stackst, stringop)

        {

            var result =newList();



            if(op =="(")

            {

                st.Push(op);

            }

            elseif(op ==")")

            {

                while(st.Peek() !="(")

                {

                    result.Add(st.Pop());

                }

                st.Pop();

            }

            else

            {

                var priority1 =IcpPriority[op];

                var priority2 =IspPriority[st.Peek()];





                while(priority2 >=priority1)

                {

                    result.Add(st.Pop());

                    priority2 =IspPriority[st.Peek()];

                }

                st.Push(op);

            }

            returnresult;

        }

 

    求值:

///

        ///进行计算

        ///


        ///

        ///

privatestaticdoubleDoCalc(Listtokens)

        {

            var st =newStack();



            foreach(var token intokens)

            {

                if(!IsOperator(token))

                {

                    //操作数入栈

st.Push(double.Parse(token));

                }

                else

                {

                    //操作符从栈顶取出两个元素进行计算,并将结果推入栈中

var operand1 =st.Pop();

                    var operand2 =st.Pop();



                    switch(token)

                    {

                        case"+":

                            st.Push(operand2 +operand1);

                            break;

                        case"-":

                            st.Push(operand2 -operand1);

                            break;

                        case"*":

                            st.Push(operand2 *operand1);

                            break;

                        case"/":

                            st.Push(operand2 /operand1);

                            break;

                        case"^":

                            st.Push(Math.Pow(operand2, operand1));

                            break;



                    }

                }

            }

            //最终栈顶元素即为表达式的值

returnst.Pop();

        }

转载于:https://www.cnblogs.com/xuweili/articles/3361084.html

更多相关:

  •  1. 指针数组 int *p[5]; [] 大于 *  2. 强制类型() 与 成员选择(./->) #include typedef struct {int data;int time; } data_t;int main() {data_t *p = (data_t *)malloc(sizeof(d...

  • 关联是jmeter中比较重要的一个点,在测试过程中有些数据是经常发生变化的,要获取这些数据,就需要使用关联,Jmeter可以通过“后置处理器”中的“正则表达式提取器”来处理关联。。 正则表达式提取器 1、在取样器下点击【添加】--【后置处理器】--正则表达式提取器     2、以随机查询城市天气为例,定义变量名称为city  ...

  • 经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?”m = 1; n = m+++m++;最近有位不相识的朋友发email给我,问为什么在某个C++系统里,下面表达式打印出两个4,而不是4和5:a = 4; cout << a++ << a;C++ 不是规定 << 操作左结合吗?是C++ 书上写错了,还是这个系统的...

  • 正则表达式的语法还包括指定选择项,对子表达式分组和引用前一子表达式的特殊字符.字符| 用于分隔供选择的字符.例如: /ab|cd|ef/ 匹配的是字符串 "ab",或者是字符串 "cd",又或者 "ef". /d{3}|[a-z]{4}/ 匹配的是要么是一个三位数,要么是四个小写字母. 在正则表达式中括号具有几种作用: 1、它的主要...

  • python基本语法有哪些? python基本语法总结: 1.Python标识符 在 Python里,标识符有字母、数字、下划线组成。 在 Python中,所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。 Python中的标识符是区分大小写的。 以下划线开头的标识符是有特殊意义的。以单下划线开头 _foo的代表不能直...

  • 我们可以重定义或重载大部分 C++ 内置的运算符。这样,就能使用自定义类型的运算符。 重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。 Box operator+(const Box&); 声明加法运算符用于把两个 Box 对...

  • 栈stack:stack 后入先出(LIFO) q.top()获取栈顶元素(并不删除)q.pop()删除栈顶元素q.push(x)向栈中加入元素q.empty()判断栈是否为空 队列queue:先入先出(FIFO)   q.front()获取队首元素(并不删除)q.pop()删除队首元素q.push(x)向队列中加入元素q....

  • resize(),设置大小(size); reserve(),设置容量(capacity); size()是分配容器的内存大小,而capacity()只是设置容器容量大小,但并没有真正分配内存。 打个比方:正在建造的一辆公交车,车里面可以设置40个座椅(reserve(40);),这是它的容量,但并不是说它里面就有了40个座椅,只能说...

  • v-for="(index,$i) in total" :key="$i":style="{left:`${itemWidth*((index-1)%rowItemCount)}px`,top:`${itemHeight*(Math.ceil(index/rowItemCount)-1)}px`}" //total是显示总数量 //l...

  •   技巧一(推荐指数★★★★★) 采用top、right、bottom、left,可以不在乎父元素的宽度和高度,对GPU损耗低于技巧三,但是对浏览器内存的消耗高于技巧三 .子元素 {/*父元素需要position: relative|absolute;*/position: absolute;margin: auto;to...

  • 设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。 push(x) – 将元素 x 推入栈中。pop() – 删除栈顶的元素。top() – 获取栈顶元素。getMin() – 检索栈中的最小元素。 示例: MinStack minStack = new MinStack(); minStack...