zoukankan      html  css  js  c++  java
  • 栈应用-中缀表达式转后缀表达式并计算值

    表达式的三种形式:
    中缀表达式:运算符放在两个运算对象中间,如:(2+1)*3。我们从小做数学题时,一直使用的就是中缀表达式。
    后缀表达式:不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则),如:2 1 + 3 。又比如3+(6-4/2)5=23的后缀表达式为:3642/-5+# (#符号为结束符)
    前缀表达式:同后缀表达式一样,不包含括号,运算符放在两个运算对象的前面,如:
    + 2 1 3 。前缀表达式和后缀表达式其实是差不多的,只不过符号位置不同而已,前缀表达式不是很常见。

    中缀转后缀表达式

    有两个栈:运算符栈和操作数栈
    简述:左括号直接压入,压入右括号,则要一直弹出到左括号(左括号舍弃)。
    压入低运算符到【运算符栈】的时候, 先将高于/等于该运算符级别的【运算符栈】栈顶运算符弹出。

    1. 从左到右扫描
    2. 若读到是操作数,则判断操作数的类型,并将操作数存入【操作数栈】
    3. 若读到的是运算符
      3.1 若为左括号“(”,则直接存入【运算符栈】
      3.2 若为右括号“)”,则弹出【运算符栈】的运算符,直到遇到左括号为止,此时抛弃该左括号。
      3.3 运算符为非括号运算符
      a. 若【运算符栈】栈顶的运算符为左括号,则直接存入【运算符栈】
      b. 若比【运算符栈】栈顶的运算符优先级高,则直接存入【运算符栈】。
      c. 若比【运算符栈】栈顶的运算符优先级低或相等,则输出栈顶运算符到【操作数栈】,直到【运算符栈】栈顶运算符低于(不包括等于)该运算符优先级或为左括号,并将当前运算符压入【运算符栈】
      ps:从【运算符】栈顶依次弹出所有【高于或等于】当前运算符的运算符,遇到左括号或低于该当前运算符的则停止,并将当前运算符压入【运算符栈】。
    4. 当读取完成后运算符栈中尚有运算符时,则依序取出运算符到【操作数栈】直到【运算符栈】为空。

    代码

    	package com.gkwind.structure.stack;
    	
    	import java.util.NoSuchElementException;
    	
    	/**
    	 * 四则运算
    	 * 队列实现操作数栈,栈实现运算符栈
    	 *
    	 * @Author thewindkee
    	 * @Date 2019/7/9 0009 下午 2:16
    	 */
    	public class MathOperation2 {
    	
    	    public MathOperation2() {
    	    }
    	
    	    public Integer calculate(String expr) {
    	        LinkedQueue<String> numStack = mapToNumStack(expr);
    	        System.out.println("numStack"+numStack);
    	        int result = exec(numStack);
    	        return result;
    	    }
    	
    	    /**
    	     * 遍历后缀表达式
    	     * 将数字压栈,遇到操作符则弹出栈顶的两个元素
    	     *
    	     * @param numStack
    	     * @return
    	     */
    	    private int exec(LinkedQueue<String> numStack) {
    	        //遇到操作符则弹出栈顶的两个元素
    	        if (numStack == null || numStack.size() < 3) {
    	            throw new IllegalArgumentException();
    	        }
    	        Stack<Integer> stack = new Stack<>();
    	        String item;
    	        while (numStack.size()>0) {
    	            item = numStack.removeFirst();
    	            if (isDigits(item)) {
    	                stack.push(Integer.parseInt(item));
    	            }else{
    	                Integer b = stack.pop();
    	                Integer a = stack.pop();
    	                //计算栈顶的两个元素,并将结果压栈
    	                stack.push(MathOpt.getOpt(item.charAt(0)).calculate(a, b));
    	            }
    	        }
    	        return stack.pop();
    	    }
    	
    	    private boolean isDigits(String item) {
    	        if (item == null || item.trim().equals("")) {
    	            return false;
    	        }
    	        for (int i = 0; i < item.length(); i++) {
    	            if (!Character.isDigit(item.charAt(i))) {
    	                return false;
    	            }
    	        }
    	        return true;
    	    }
    	
    	    private LinkedQueue<String> mapToNumStack(String expr) {
    	        Stack<Character> optStack = new Stack<Character>();
    	        LinkedQueue<String> numQueue = new LinkedQueue<String>();
    	        //1.从左到右扫描
    	        boolean numEnd = false;
    	        StringBuilder tempNum = new StringBuilder();
    	        for (int i = 0; i < expr.length(); i++) {
    	            final char c = expr.charAt(i);
    	            if (Character.isDigit(c)) {
    	                //2.若读到是操作数,先存在tempNum中
    	                tempNum.append(c);
    	//                numQueue.addLast(String.valueOf(c));
    	            }else{
    	                //3.若读到的是运算符
    	                //tempNum是否有数字,一并存入【操作数栈】
    	                if (tempNum.length()>0) {
    	                    numQueue.addLast(tempNum.toString());
    	                    tempNum.delete(0, tempNum.length());
    	                }
    	                MathOpt mathOpt = MathOpt.getOpt(c);
    	                dealMathOpt(mathOpt, optStack, numQueue);
    	            }
    	        }
    	        if (tempNum.length()>0) {
    	            numQueue.addLast(tempNum.toString());
    	            tempNum.delete(0, tempNum.length());
    	        }
    	
    	        //4.当表示读取完成后运算符栈中尚有运算符时,则依序取出运算符到【操作数栈】直到【运算符栈】为空。
    	        while (optStack.size() != 0) {
    	            numQueue.addLast(String.valueOf(optStack.pop()));
    	        }
    	        return numQueue;
    	    }
    	
    	    private void dealMathOpt(MathOpt currentMathOpt, final Stack<Character> optStack, final LinkedQueue<String> numQueue) {
    	        if (currentMathOpt == null || optStack == null || numQueue == null) {
    	            throw new NullPointerException();
    	        }
    	        //3.若读到的是运算符
    	        Character headOptChar = optStack.peek();
    	        MathOpt headMathOpt = headOptChar==null?null: MathOpt.getOpt(headOptChar);
    	        if(currentMathOpt.priority == Priority.HIGH){//括号运算符
    	            if (currentMathOpt == MathOpt.LEFT_BRACKET) {
    	                //3.1 若为左括号“(”,则直接存入【运算符栈】
    	                optStack.push(currentMathOpt.opt);
    	            }else{
    	                //3.2 若为右括号“)”,则输出【运算符栈】的运算符,直到遇到左括号为止,此时抛弃该左括号。
    	                while (true) {
    	                    Character nextOptChar = optStack.peek();
    	                    if (nextOptChar == null) {
    	                        throw new IllegalArgumentException("缺少左括号");
    	                    }
    	                    MathOpt nextHeadMathOpt = MathOpt.getOpt(nextOptChar);
    	                    if (nextHeadMathOpt.equals(MathOpt.LEFT_BRACKET)) {
    	                        //栈顶为左括号 ,丢弃
    	                        optStack.pop();
    	                        break;
    	                    }
    	                    numQueue.addLast(String.valueOf(optStack.pop()));
    	                }
    	            }
    	        }else {//非括号运算符
    	            if (headMathOpt!=null && MathOpt.LEFT_BRACKET.opt == headMathOpt.opt) {
    	                //a.若【运算符栈】栈顶的运算符为左括号,则直接存入【运算符栈】
    	                optStack.push(currentMathOpt.opt);
    	            } else if (headMathOpt==null||currentMathOpt.priority.ordinal()> headMathOpt.priority.ordinal()) {
    	                //b.若比【运算符栈】栈顶的运算符优先级高,则直接存入【运算符栈】。
    	                optStack.push(currentMathOpt.opt);
    	            } else  {
    	                // ps:从【运算符】栈顶依次弹出所有【高于或等于】当前运算符的运算符,遇到左括号或低于该当前运算符的则停止。
    	                //c.若比【运算符栈】栈顶的运算符优先级低或相等,则输出栈顶运算符到【操作数栈】,
    	                // 直到【运算符栈】栈顶运算符低于(不包括等于)该运算符优先级或为左括号,并将当前运算符压入【运算符栈】
    	                numQueue.addLast(String.valueOf(optStack.pop()));
    	                while (true) {
    	                    Character nextOptChar = optStack.peek();
    	                    if (nextOptChar == null) {
    	                        //栈顶已经不存在运算,将当前运算法压入【运算符栈】
    	                        optStack.push(currentMathOpt.opt);
    	                       break;
    	                    }
    	                    MathOpt nextHeadMathOpt = MathOpt.getOpt(nextOptChar);
    	
    	                    if (nextHeadMathOpt.equals(MathOpt.LEFT_BRACKET)) {
    	                        //栈顶为左括号 ,停止
    	                        //当前运算法压入【运算符栈】
    	                        optStack.push(currentMathOpt.opt);
    	                        break;
    	                    }else if(currentMathOpt.priority.ordinal() <= nextHeadMathOpt.priority.ordinal()){
    	                        //比栈顶运算符小的,输出到【操作数栈】
    	                        numQueue.addLast(String.valueOf(optStack.pop()));
    	                    }else{
    	                        //比栈顶运算符大的,将当前运算符压入【运算符栈】,并退出
    	                        optStack.push(currentMathOpt.opt);
    	                        break;
    	                    }
    	                }
    	            }
    	
    	        }
    	
    	    }
    	
    	    enum MathOpt {
    	        ADD(Priority.LOW, '+'){
    	            @Override
    	            int calculate(int a, int b) {
    	                return a+b;
    	            }
    	        },MINUS(Priority.LOW,'-')
    	            {
    	                @Override
    	                int calculate(int a, int b) {
    	                    return a-b;
    	                }
    	            },MULTI(Priority.MIDDLE,'*')
    	            {
    	                @Override
    	                int calculate(int a, int b) {
    	                    return a*b;
    	                }
    	            },DIV(Priority.MIDDLE,'/'){
    	            @Override
    	            int calculate(int a, int b) {
    	                return a/b;
    	            }
    	        },
    	        LEFT_BRACKET(Priority.HIGH, '('){
    	            @Override
    	            int calculate(int a, int b) {
    	                throw new IllegalArgumentException();
    	            }
    	        }, RIGHT_BRACKET(Priority.HIGH, ')'){
    	            @Override
    	            int calculate(int a, int b) {
    	                throw new IllegalArgumentException();
    	            }
    	        },;
    	        private final Priority priority;
    	        private final char opt;
    	        MathOpt(Priority priority, char opt) {
    	            this.priority=priority;
    	            this.opt = opt;
    	        }
    	
    	        static MathOpt getOpt(char opt) {
    	            for (MathOpt mathOpt : MathOpt.values()) {
    	                if (mathOpt.opt == opt) {
    	                    return mathOpt;
    	                }
    	            }
    	            return null;
    	        }
    	
    	        abstract int calculate(int a, int b);
    	
    	    }
    	    enum Priority{
    	        LOW(),MIDDLE(),HIGH();
    	//        LOW(1),MIDDLE(2),HIGH(3);
    	//        private final int level;
    	//        Priority(int i) {
    	//            this.level = i;
    	//        }
    	    }
    	
    	    static class  LinkedQueue<E> {
    	
    	        private Stack.Node<E> head;
    	        private Stack.Node<E> tail;
    	        private int count=0;
    	        private static class Node<E> {
    	            E data;
    	            Stack.Node<E> prev;
    	            Stack.Node<E> next;
    	            public Node(E data, Stack.Node<E> prev, Stack.Node<E> next) {
    	                this.data = data;
    	                this.prev = prev;
    	                this.next = next;
    	            }
    	            @Override
    	            public String toString() {
    	                final StringBuilder sb = new StringBuilder("{");
    	                sb.append(""data":")
    	                    .append(data);
    	                sb.append(","prev":")
    	                    .append(prev);
    	                sb.append(","next":")
    	                    .append(next);
    	                sb.append('}');
    	                return sb.toString();
    	            }
    	        }
    	
    	        public <E> LinkedQueue() {
    	        }
    	
    	        public void addLast(E e) {
    	            if (tail == null) {
    	                head = tail = new Stack.Node<>(e, null, null);
    	            }else{
    	                Stack.Node<E> node = new Stack.Node<>(e, null, null);
    	                tail.next=node;
    	                node.prev = tail;
    	                tail = node;
    	            }
    	            count++;
    	        }
    	
    	        public E removeFirst() {
    	            checkNotEmpty();
    	            final Stack.Node<E> headNode = this.head;
    	            final E data = headNode.data;
    	            Stack.Node<E> next = head.next;
    	            head=next;
    	            count--;
    	            //help gc
    	            if(next!=null)next.prev=null;
    	            headNode.data=null;
    	            headNode.next=null;
    	            return data;
    	        }
    	
    	        private void checkNotEmpty() {
    	            if (count == 0) {
    	                throw new NoSuchElementException();
    	            }
    	        }
    	
    	        public int size(){
    	            return count;
    	        }
    	
    	        @Override
    	        public String toString() {
    	            final StringBuilder sb = new StringBuilder("{");
    	            sb.append("size: ").append(size()).append(",data:[");
    	            Stack.Node<E> node = head;
    	            while (node != null) {
    	                sb.append(node.data);
    	                node = node.next;
    	                if (node == null) {
    	                    break;
    	                }
    	                sb.append(",");
    	            }
    	            sb.append("]}");
    	            return sb.toString();
    	        }
    	    }
    	    
    	    public static void main(String[] args) {
    	        LinkedQueue<Integer> queue = new LinkedQueue<>();
    	        queue.addLast(3);
    	        queue.addLast(4);
    	        queue.addLast(5);
    	//        queue.addLast(7);//exception
    	        System.out.println(queue);
    	        System.out.println(queue.removeFirst());
    	        System.out.println(queue);
    	        System.out.println(queue.removeFirst());
    	        System.out.println(queue);
    	        System.out.println(queue.removeFirst());
    	        System.out.println(queue);
    	//        System.out.println(queue.removeFirst());//exception
    	//        System.out.println(queue);
    	
    	        //test stack
    	        Stack<Integer> stack = new Stack<>();
    	        stack.push(1);
    	        stack.push(2);
    	        stack.push(3);
    	//        stack.push(4);//
    	        System.out.println(stack);
    	        System.out.println(stack.peek());
    	        System.out.println(stack.pop());
    	        System.out.println(stack);
    	        System.out.println(stack.pop());
    	        System.out.println(stack);
    	        System.out.println(stack.pop());
    	        System.out.println(stack);
    	//        System.out.println(stack.pop());//exception
    	
    	        System.out.println(new MathOperation2().calculate("9+(3-1)*3+10/2")==9+(3-1)*3+10/2);
    	        System.out.println(new MathOperation2().calculate("(1+2)*3-4*5/2")==(1+2)*3-4*5/2);
    	        System.out.println(new MathOperation2().calculate("(1+2)*(5*(3-4)*2)*5/4")==(1+2)*(5*(3-4)*2)*5/4);
    	
    	    }
    	
    	    /**
    	     * 先进后出
    	     * @param <E>
    	     */
    	    static class Stack<E>{
    	
    	        private Node<E> head;
    	        private Node<E> tail;
    	        private int count=0;
    	//        private int capacity;
    	        private static class Node<E>{
    	            E data;
    	            Node<E> prev;
    	            Node<E> next;
    	            public Node(E data,Node<E> prev, Node<E> next) {
    	                this.data = data;
    	                this.prev = prev;
    	                this.next = next;
    	            }
    	            @Override
    	            public String toString() {
    	                final StringBuilder sb = new StringBuilder("{");
    	                sb.append(""data":")
    	                    .append(data);
    	                sb.append(","prev":")
    	                    .append(prev);
    	                sb.append(","next":")
    	                    .append(next);
    	                sb.append('}');
    	                return sb.toString();
    	            }
    	        }
    	
    	        public Stack() {
    	        }
    	
    	        public void push(E e) {
    	//            checkNotFull();
    	            count ++;
    	            if (tail == null) {
    	                head = tail = new Node<>(e,null, null);
    	            }else{
    	                Node<E> node = new Node<>(e, null, null);
    	                tail.next = node;
    	                node.prev = tail;
    	                //newNode become tail Node
    	                tail = node;
    	            }
    	        }
    	
    	//        private void checkNotFull() {
    	//            if(count==capacity)throw new IndexOutOfBoundsException();
    	//        }
    	        private void checkNotEmpty() {
    	            if(count==0) throw new NoSuchElementException();
    	        }
    	
    	        public E peek() {
    	            return tail==null?null:tail.data;
    	        }
    	
    	        public E pop() {
    	            checkNotEmpty();
    	            final Node<E> tailNode = tail;
    	            Node<E> prev = tailNode.prev;
    	            final E data = tailNode.data;
    	
    	            tailNode.prev = null;
    	            if (prev == null) {
    	                //tailNode is headNode
    	                head = null;
    	            } else {
    	                prev.next = null;
    	            }
    	            //help gc
    	            tailNode.prev = null;
    	            tailNode.data = null;
    	
    	            tail = prev;
    	            count--;
    	            return data;
    	        }
    	
    	        public int size(){
    	            return count;
    	        }
    	
    	        @Override
    	        public String toString() {
    	            final StringBuilder sb = new StringBuilder("{");
    	            sb.append("size:").append(count).append(",data:[");
    	            Node<E> node = head;
    	            while (node!=null) {
    	                sb.append(node.data);
    	                node=node.next;
    	                if (node == null) {
    	                    break;
    	                }
    	                sb.append(",");
    	            }
    	            sb.append("]}");
    	            return sb.toString();
    	        }
    	    }
    	}
    
  • 相关阅读:
    资源:mysql下载路径
    知识点:jar包与war包的差异
    Linux:jar服务部署
    Flyway:Spring Boot中使用Flyway来管理数据库版本
    Java:Java控制台输出保存进文件
    Maven:手动添加jar包进Maven本地库内
    Https:SSL双向认证机制(理论知识)
    Linux ubuntu 下寻找 texlive 缺失包 texlive 缺失包(转载)
    Android应用开发提高篇(4)-----Socket编程(多线程、双向通信)(转载)
    Android应用开发基础篇(12)-----Socket通信(转载)
  • 原文地址:https://www.cnblogs.com/thewindkee/p/12873129.html
Copyright © 2011-2022 走看看