其他分享
首页 > 其他分享> > 小妹使用栈实现计算器(下)

小妹使用栈实现计算器(下)

作者:互联网

接上篇:小妹使用栈实现计算器(上)

我:好饱,怎么样,写出来了么?看你那愁眉哭脸,指定没写出来。

小妹:你很开心?

我:哈哈哈,也不是,就是吃饱了,舒坦。

小妹:找打呢你!

我:我来给你讲,别急别急。

小妹:你这脸变的可比翻书快啊。

我:额...抓紧时间,搞完你去吃饭多好。

思路给你了,哪里不明白。

小妹:带括号的,我不知道怎么实现。。。明明按照思路写的,就是不对。

我:带括号的,遇见左括号,你就直接入栈(这里也需要考虑括号内的运算符的优先级),遇见右括号,直接弹出s1的元素,将出栈的元素压入到s2,直到遇见左括号为止。直到分割的元素全部入栈,然后将s1的元素逐个出栈,按出栈的顺序压入s2中,然后输出的逆序即为逆波兰表达式。

(附上篇思路)

 思路分析:
     1.初始化两个栈;运算符栈s1和存储中间结果的栈s2
     2.从左至右扫描中缀表达式
     3.遇到操作符时,将其压入s2
     4.遇到运算符时,比较其与s1栈顶运算符的优先级
        1.如果s1为空,或栈顶运算符为左括号,则直接将此运算符入栈
        2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
        3.否则,将s1栈顶的运算符弹出并压入s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
     5.遇到括号:
        1.如果是左括号,直接压入s1
        2.如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,
          此时将这一对括号丢弃
     6.重复步骤2-5,直到表达式的最右边
     7.将s1中剩余的运算符依次弹出并压入s2
     8.依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

现在你明白了吗?

小妹:这样啊。那我去实现一下,试一试。

public class CenterSwapSuffer {

    Stack s1;
    Stack s2;
    public String swap(@NotNull String s) {
        s1 = new Stack(s.length()+1);
        s2 = new Stack(s.length()+1);
        Stack split = this.split(s);
        String[] c = new String[split.getLength() + 1];
        int length = split.getLength() +1;
        for (int i = length - 1; i >= 0; i--) {
            c[i] = split.pop() + "";
        }

        for (String symbol : c) {

            if (symbol.equals("(") || symbol.equals("+") || symbol.equals("-") || symbol.equals("*") || symbol.equals("/")) {
                while (true) {
//                    如果符号栈顶是左括号,或者为空直接让符号入栈
                    if (s1.peek() == null || symbol.equals("(") || s1.peek().equals("(")) {
                        s1.push(symbol);
                        break;
                    } else {

                        String peek = (String) s1.peek();
//                        s1栈顶符号优先权
                        int p1 = this.priority(peek);
//                        当前符号优先权
                        int p2 = this.priority(symbol);
//                        当前符号优先权大
                        if (p2 > p1) {
                            s1.push(symbol);
                            break;
                        } else {
//                        弹出s1栈顶符号,并且压入s2中
                            String peekSymbol = (String) s1.pop();
                            s2.push(peekSymbol);
                        }
                    }
                }
//                如果是右括号,就挨个弹出,再push进s2
            } else if (")".equals(symbol)){

                while (true) {
//                    在遇见左括号后,将左括号弹出,并结束循环
                    if (s1.peek().equals("(")) {
                        s1.pop();
                        break;
                    }

                    String popS1 = (String)s1.pop();
                    s2.push(popS1);
                }
            } else {
                s2.push(symbol);
            }
        }

        while (s1.peek() != null) {
            s2.push(s1.pop());
        }
        Object[] o = new Object[s2.getLength()+1];
        for (int i = 0; i < o.length; i++) {
            if (s2.peek() == null) break;
            o[i] = s2.pop();
        }

//        逆序输出返回
        String s3 = "";
        for (int i = o.length - 1; i >= 0 ; i--) {
            s3 += o[i] + " ";
        }
        return s3;
    }

    /**
     * 计算运算符的优先级
     * @param s : 符号
     * @return : 返回数字大的运算符优先级更高
     */
    private int priority(@NotNull String s) {
        int result = 0;
        if ("+".equals(s) || "-".equals(s)) {
            result = 1;
        } else if ("*".equals(s) || "/".equals(s)) {
            result = 2;
        } else {
            throw new RuntimeException("illegal symbol");
        }
        return result;
    }

    /**
     * 分析字符串,将一个字符串转换为一个个的数字和运算符,并且入栈,方便计算
     * @param s : 传入要计算的字符串
     * @return : 返回一个分割后栈
     */
    private Stack split(@NotNull String s) {
//        定义栈的大小,字符串分割之后存入栈中,最多不会超过字符串的最大长度。
//        所以初始化栈的大小为s.length()+1
        Stack stack = new Stack(s.length() + 1);
        int i = 0;
        String str = "";
        while (s.length() > i) {
//            挨个遍历字符串的内容
            char c = s.charAt(i);
//            判断字符串分割出来的字符是否为数字(jdk的api)
            if (Character.isDigit(c)) {
//                如果是数字,就拼接成String,不压入栈
                str += c;
            } else {
//                如果字符不是数字,证明一个数字已经拼接完成
//                在压入符号之前压入该数字,并且清空字符串,方便下次拼接
                if (str != "") {
                    stack.push(str);
                    str = "";
                }
//                符号入栈
                stack.push(c);
            }
            i++;
        }
//        遍历到最后,如果str不为空,则是最后一个数字,压入栈中即可
        if (str != "") {
            stack.push(str);
        }
        return stack;
    }
}

你看这样写,对不对。

(字符串加入一个" " 空字符,是为了后面便于分割)

我:恩。。算是对了。不错不错,值得表扬。

小妹:拉到吧,我先去吃饭,回来再讲,给我讲一个计算器,你说了这么多,都没讲重点呢。

我:这都是重点,去吃饭,抓紧回来。

小妹:我的鸡蛋咋办?

我:下次一定,下次一定。

小妹:.....

...(30分钟后)

小妹:啊!好饱,舒坦。

我:既然吃饱了,继续吧。

小妹:想午休。

我:你在做梦,继续。

刚刚写完了表达式的转换,那么我们来讲讲怎么实现计算器吧。

小妹:啊,终于进入正题了。快说快说。

我:好,别着急,相反,这才是比较简单的步骤。刚刚的swap方法,不是返回了一个字符串吗?我们用jdk的split方法去分割一下这个字符串,这样

    /**
     * 分割逆波兰表达式
     * @param str : 逆波兰表达式的串
     * @return : 逆波兰表达式存放的List集合
     */
    public List<String> splitString(String str) {
        List<String> list = new ArrayList<>();
        String[] split = str.split(" ");
        for (String s : split) {
            list.add(s);
        }
        return list;
    }

然后再遍历这个字符串,利用栈进行求结果,如下:

public class SufferExpress {

    private Stack stack = new Stack(10);

    /**
     * 分割逆波兰表达式
     * @param str : 逆波兰表达式的串
     * @return : 逆波兰表达式存放的List集合
     */
    public List<String> splitString(String str) {
        List<String> list = new ArrayList<>();
        String[] split = str.split(" ");
        for (String s : split) {
            list.add(s);
        }
        return list;
    }

    public int cal(List<String> list) {
        for (String s : list) {
//            如果s是数字,就进行入栈
            if (s.matches("\\d+")) {
                stack.push(Integer.parseInt(s));
            } else {
                int p1 = (Integer) stack.pop();
                int p2 = (Integer)stack.pop();
                int result = this.calVal(s, p1, p2);
                stack.push(result);
            }
        }
        System.out.println("result = " + stack.peek());
        return (int)stack.peek();
    }
//    calculate value
    private int calVal(String c, int p1, int p2) {
        int res = 0;
        if ("+".equals(c)) {
            res = p1 + p2;
        } else if ("-".equals(c)) {
            res = p2 - p1;
        } else if ("*".equals(c)) {
            res = p2 * p1;
        } else if ("/".equals(c)) {
            res = p2 / p1;
        } else {
            throw new RuntimeException("符号有误!");
        }
        return res;
    }
}

注意一点,由于栈是先进后出,所以在做减法和除法运算的时候,应该是后弹出的减去先弹出的,而加法和乘法没有区别。

你看这样写,简单吧。

小妹:让我研究一下,别着急。

(10分钟后)

(哇,太简单了,你太厉害了小哥哥。)

恩。。还不错,诶,诶,你发什么呆啊?

我:啊,额,你不夸我么?

小妹:夸你干嘛,这么简单,哈哈哈!

 我:行吧行吧。这下懂了吧,别再用你那简单的代码拿出来丢人嗷!

小妹:滚!!!

我:好勒。

代码亲测,无错误,待优化,如有指点,万分感谢!

标签:String,实现,s2,s1,equals,运算符,int,小妹,计算器
来源: https://blog.csdn.net/weixin_44231805/article/details/118785567