【数据结构】计算器

这里写图片描述

注: 除中缀表达式和后缀表达式算法外, 其余计算器的功能算法本人自己实现, 可能并不规范和标准, 并且可能存在一些BUG, 仅供参考.

平常我们数学中所计算的表达式 :

1.5+2*3+(5-6/2)= ?

如果在计算机中,应该如何计算该表达式?

涉及问题

  1. 根据运算符优先级,确定运算顺序。应先算括号里面的,后算括号外的,先算次方开方,后算乘除,最后算加减。
  2. 处理表达式错误问题,如出现其他字符,或者括号不匹配
  3. 功能完备,加入计算小数,负数,超大数的功能。

运算顺序

中缀表达式和后缀表达式

平常我们计算的表达式也称中缀表达式,20世纪50年代,波兰逻辑学家Jan也遇到了运算优先级的问题,后来他发明了后缀表达式,解决了该问题。

中缀表达式 :1.5+2*3+(5-6/2)

转为后缀表达式 : 1.5 2 3 * + 5 6 2 / - +

计算后缀表达式

计算需要用到数据结构里的栈

依次将后缀表达式的每个数字入栈
这里写图片描述
遇到运算符,从栈里出栈两个元素进行运算,随后再入栈
这里写图片描述
这里写图片描述
按照此规则,继续进行下去
这里写图片描述
这里写图片描述
这里写图片描述

最后得出答案 9.5

1.5+2*3+(5-6/2) = 9.5 可以口算一下

相比起中缀表达式, 这样一步一步对后缀表达式的字符进行处理, 很容易用计算机的编程语言来实现

如何转换后缀表达式

同样要用到栈结构.

转换运算规则如下:

  1. 遇到数字的话直接输出。
  2. 创建一个符号栈,遇到符号的话进栈.。同时需要设置符号优先级。因为先算加减,后算乘除,所以先假设 + - 优先级为 1, * / 优先级为为 2。 则在符号进栈时, 如果栈里有优先级大于或等于它( >= )的符号,则这些符号全部按顺序出栈,之后该符号再进栈。
  3. 遇到括号,左括号的话不做判别直接入栈,右括号不入栈,且要一直输出栈内的符号,直到遇到左括号为止。

转换1.5+2*3+(5-6/2)为后缀表达式

遇到数字输出, 遇到符号进栈。
1.5输出 + 进栈 2输出
这里写图片描述

之后 * 进栈, 因为+的优先级不大于也不等于( >= ) * ,所以 + 不出栈 * 直接进栈。
之后 3 输出
这里写图片描述
这里写图片描述

之后是 + 号, 因为 栈里的 * 和 + 的优先级都大于或等于它( >= ), 所以* 出栈 + 出栈, 之后该 + 号再进栈
这里写图片描述

按此规则继续下去,遇到左括号( 直接入栈。

这里写图片描述
注: 这里入栈 - 号时虽然栈里有+号,但+号在括号的后面,所以不做判断,不用出栈。

遇到右括号,则一直输出栈里内容直到左括号。

这里写图片描述
最后再输出+号 ,即可得到后缀表达式

1.5 2 3 * + 5 6 2 / - +

中缀转后缀表达式挨个检索每一个字符,之后进行处理,这种过程也适合用编程语言实现。

###中缀转后缀时处理小数,超大数和负数

处理小数 超大数

处理数字时需要循环处理
例如读入 175.2
首先创建一个空串 StringBuilder 循环判断 逐个字符读入 如果读入的是数字或小数点,则从末尾拼接到字符串里, 直到不为数字或小数点为止,最后如果字符串不为空, 则 调用 new BigDecimal(String str) 构造器把其构造成超大数.在加入栈里

	String item = 175.2;
	StringBuilder SB = new StringBuilder("");
    while (isNumber(c) || c == '.')
    {
        SB.append(c);
        if (++i >= item.length()) break;
        c = item.charAt(i);
    }
    if (!SB.toString().equals(""))
	{   // StringBuilder没有重写equlas 需要调用String的eqluals
        stack.push(new BigDecimal(SB.toString()));
        // bigDecimal 超大浮点数
    }

处理负数

判断负号如果上一个字符读取的是运算符号,则给下一个读取到的数取反

例如 3*-2, 定义一个布尔类型的开关值key 每次循环默认 false, 如果取到了一个符号,则 key = true, 如果key= true 且取到了 - 号 则在处理数字的字符串 StringBuilder上先拼接一个负号,之后继续取值

 StringBuilder SB = new StringBuilder("");
    if (c == '-' && key)
 {
    ++i;
    if(i < item.length())
    c = item.charAt(i);
    SB.append('-');
  }
   while (isNumber(c) || c == '.')
   {
   ......
   }

表达式错误

括号不匹配

括号匹配同样需要用到栈

例如表达式 1+2*(((1+3)+2)+4)

挨个字符遍历表达式,
遇到左括号 ‘’(’’ 则入栈, 遇到右括号则从栈里出一个括号, 如果栈为空则不匹配, 一直进行下去. 到最后所有括号都遍历过且栈为空则说明括号匹配,否则括号不匹配.

出现错误字符

开始时遍历一遍字符串, 遇到不符合算数的字符删除即可

自己的实现

加入了次方运算^ 调用的是Math.pow (double, double)
超大数不能处理无线循环小数, 除法运算时需特殊处理,可以转成double类型运算.

代码

package JavaTest.caculater;


import java.math.BigDecimal;
import java.util.Scanner;
import java.util.Stack;

public class Caculater {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String item = scanner.nextLine();
        Stack stack = new Stack();
        Stack stack1 = new Stack();
        boolean key = false;
        if(!pi(item))
        {
            System.out.println("括号不匹配");
            return;
        }
        for(int i = 0; i < item.length(); i++)
        {

            char c = item.charAt(i);
            while (!isfu(c) && !isNumber(c))
            {
                ++i;
                if(i < item.length())
                c = item.charAt(i);
                if( i == item.length())
                    break;
            }
            if( i == item.length())
                break;
            StringBuilder SB = new StringBuilder("");
            if (c == '-' && key)
            {
                ++i;
                if(i < item.length())
                    c = item.charAt(i);
                SB.append('-');
            }
            while (isNumber(c) || c == '.')
            {
                key = false;
                SB.append(c);
                if (++i >= item.length()) break;
                c = item.charAt(i);
            }
            if (!SB.toString().equals("")){
                stack.push(new BigDecimal(SB.toString()));

            }
            if (c == '^')
            {

                if((!stack1.empty()))
                    if( ((char)stack1.peek() == '^'))
                    {
                        stack.push(stack1.pop());
                    }
            }
            if (c == '*' || c == '/' || c == '+' || c == '-')
            {

                if((!stack1.empty()))
                    if( ((char)stack1.peek() == '*') || ((char)stack1.peek() == '/') || ((char)stack1.peek() == '^'))
                    {
                       stack.push(stack1.pop());
                    }
            }
            if( c == '+' || c == '-')
            {
                if((!stack1.empty()))
                    if( ((char)stack1.peek() == '*') || ((char)stack1.peek() == '/') || ((char)stack1.peek() == '+') || ((char)stack1.peek() == '-') || ((char)stack1.peek() == '^'))
                    {
                        stack.push(stack1.pop());
                    }
            }
            if (isfu(c))
            {
                stack1.push(c);
                key = true;
            }
            if(c == ')')
            {
                char cha = c;
                while (cha != '(') {
                     cha = (char) stack1.pop();
                     if(cha != '(' && cha != ')')
                         stack.push(cha);
                }
            }
        }
        while (!stack1.empty())
        {
            stack.push(stack1.pop());
        }
        Stack stack2 = new Stack<>();

        while (!stack.empty())
        {
            stack2.push(stack.pop());
        }
        while (!stack2.empty()) {
            Object c = stack2.pop();
//            System.out.println(c.getClass() + " " + c);
            if (c.getClass() == BigDecimal.class) {
                stack1.push((BigDecimal) c);
//                System.out.println(1);
                continue;
            }
            else
            {
//                System.out.println(2);
                if ((char) c == '*' && (stack1.size() != 1))
                    stack1.push( ( (BigDecimal)stack1.pop() ).multiply( (BigDecimal) stack1.pop() ) );
                if ((char) c == '^' && (stack1.size() != 1)) {
                    BigDecimal a = (BigDecimal) stack1.pop();
                if(a.intValue() >= 1)
                    stack1.push(((BigDecimal) stack1.pop()).pow(a.intValue()));
                    else {
                        stack1.push(new BigDecimal(Math.pow(((BigDecimal) (stack1.pop())).doubleValue(), a.doubleValue())));
                   }
                }
                if ((char) c == '/' && (stack1.size() != 1)) {
                    Double a = ((BigDecimal) stack1.pop()).doubleValue();
                    stack1.push (new BigDecimal( ( (Double) ( ( (BigDecimal)stack1.pop() ).doubleValue() / a)).toString()));
                }
                if ((char) c == '+' && (stack1.size() != 1))
                    stack1.push(( (BigDecimal)stack1.pop() ).add( (BigDecimal) stack1.pop() ));
                if ((char) c == '-')
                {
                    BigDecimal a = (BigDecimal) stack1.pop();
                    if(!stack1.empty() && stack1.peek().getClass() != char.class )
                    {
                        stack1.push(((BigDecimal) stack1.pop()).subtract(a));
                    }
                    else
                        stack1.push((a).multiply(new BigDecimal(-1)));
                }
            }
        }
        System.out.printf("%.6s",stack1.pop());
    }
    public static boolean isNumber(char c)
    {
        if (c >= '0' && c <= '9')
            return true;
        else
            return false;
    }
    public static boolean isfu(char c)
    {
        if(c == '+' || c == '-' || c == '*'|| c == '/' || c == '(' || c == '^')
            return true;
        else return false;
    }

    public static boolean pi(String string)
    {
        Stack<Character> stack = new Stack<Character>();
        for(int i = 0; i < string.length(); i++)
        {

            if(string.charAt(i) == '(')
                stack.push('(');
            if(string.charAt(i) == ')')
                if(stack.empty() || stack.pop() != '(')
                    return false;
        }
       if(!stack.empty())
           return false;
        return true;
    }

}

测试运行

超大数
这里写图片描述
负数次方
这里写图片描述
开方
这里写图片描述
小数负数
这里写图片描述
括号匹配
这里写图片描述
错误字符删除处理
这里写图片描述

BUG肯定还存在, 后续还需继续改善。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值