Data structure and algorithm 2: stack

Keywords: calculator less

1: stack

A practical requirement of 1.1 stack

Please enter an expression

Calculation formula: [7 * 2 * 2-5 + 1-5 + 3-3] Click to calculate [as shown below]

 

Excuse me: how does the bottom layer of the computer calculate the result? Note that it's not a simple operation to list the formula, because we look at the formula 7 * 2 * 2 - 5, but how does the computer understand the formula (for the computer, what it receives is a string), we are talking about this problem. > stack

1.2 introduction to stack

  1. Stack (stack)
  2. A stack is an ordered list of Filo first in last out.
  3. Stack is a kind of special linear table that can only insert and delete elements at the same end of linear table. One end that can be inserted or deleted is the changing end, which is called top, and the other end is the fixed end, which is called bottom.
  4. According to the definition of stack, the first element in the stack is at the bottom of the stack, the last element is at the top of the stack, while the deletion element is just the opposite. The last element is deleted first, and the first element is deleted last.
  5. The concepts of pop and push (as shown in the figure)

1.3 application scenario of stack

  1. Subroutine call: before jumping to a subroutine, the address of the next instruction will be stored in the stack until the subroutine is executed, and then the address will be taken out to return to the original program.  
  2. Handling recursive calls: similar to subroutine calls, except that in addition to storing the address of the next instruction, data such as parameters and area variables are also stored in the stack.
  3. Expression conversion [infix expression to suffix expression] and evaluation (practical solution).
  4. Traversal of binary tree.
  5. The depth first search method of graph.

1.4 quick start to stack

① use array to simulate the use of stack, because the stack is an ordered list, of course, you can use the array structure to store the data content of the stack. Next, we use array to simulate the stack out, in and so on.

② analyze the implementation ideas and draw a schematic diagram

③ simulate stack with linked list

Code implementation:

Use array to simulate stack

/**
 * @author Wnlife
 * @create 2019-10-20 21:29
 * <p>
 * Implementing stack with array
 */
public class ArrayStackDemo {

    public static void main(String[] args) {

        ArrayStack stack = new ArrayStack(5);
        String key = "";
        boolean loop = true;//Control whether to exit the menu
        Scanner scanner = new Scanner(System.in);
        while (loop) {

            System.out.println("show:Represents data in the display stack");
            System.out.println("push:Indicates adding data to the stack");
            System.out.println("pop:Pop up the data at the top of the stack");
            System.out.println("exit:Exit procedure");

            key = scanner.next();
            switch (key) {
                case "show":
                    stack.showStack();
                    break;
                case "push":
                    System.out.println("Please enter the data to be added~~");
                    int val = scanner.nextInt();
                    stack.push(val);
                    break;
                case "pop":
                    try {
                        System.out.printf("The data out of the stack is%d\n", stack.pop());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("Program exit~~~");
    }
}


/**
 * Stack implemented by array
 */
class ArrayStack {

    private int maxSize;//Stack size
    private int[] stack;//Array simulation stack, where data is stored
    private int top = -1;//Top of stack

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }

    //Judge whether the stack is empty
    public boolean isEmpty() {
        return top == -1;
    }

    //Judge whether the stack is full
    public boolean isFull() {
        return top == maxSize - 1;
    }

    //Stack -push
    public void push(int num) {

        if (isFull()) {
            System.out.println("The stack is full.~~");
            return;
        }
        stack[++top] = num;
    }

    //Stack -pop
    public int pop() {

        if (isEmpty()) {
            throw new RuntimeException("The stack is empty.~~");
        }
        int val = stack[top];
        top--;
        return val;
    }

    //Display the stack situation [traverse the stack], when traversing, you need to display data from the top of the stack
    public void showStack() {

        if (isEmpty()) {
            System.out.println("Stack empty, no data~~");
            return;
        }
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

}

Use linked list to simulate stack:

/**
 * @author Wnlife
 * @create 2019-10-21 15:23
 * <p>
 * Implementing a stack with linked list
 */
public class LinkedListStackDemo {

    public static void main(String[] args) {

        LinkedListStack stack = new LinkedListStack(5);
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;//Cycle control variable
        String key = "";
        while (loop) {

            System.out.println("show:Indicates to view the data in the stack~~");
            System.out.println("push:Means to push data into the stack~~");
            System.out.println("pop:Indicates to pop up the data on the top of the stack~~");
            System.out.println("exit:Exit program~~");
            key = scanner.next();
            switch (key) {
                case "show":
                    stack.show();
                    break;
                case "push":
                    System.out.println("Please input data:");
                    int val = scanner.nextInt();
                    stack.push(val);
                    break;
                case "pop":
                    try {
                        int topVal = stack.pop();
                        System.out.printf("The data out of the stack is%d\n", topVal);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("Program exit~~");
    }

}


/**
 * Creating a stack using linked lists
 */
class LinkedListStack {

    private Node top;
    private int maxSize;//Stack size
    private int size;

    public LinkedListStack(int maxSize) {
        this.maxSize = maxSize;
        create(maxSize);
    }

    //Create stack
    public void create(int num) {
        Node pre = null;
        for (int i = 0; i < num; i++) {
            Node node = new Node();
            if (i == 0) {
                top = node;
            } else {
                pre.next = node;
            }
            pre = node;
        }
    }

    //Determine if the stack is full
    public boolean isFull() {
        return size == maxSize;
    }

    //Judge whether the stack is empty
    public boolean isEmpty() {
        return size == 0;
    }

    //Push data onto the stack
    public void push(int num) {
        if (isFull()) {
            System.out.println("The stack is full.~~");
            return;
        }
        Node node = new Node(num);
        node.next = top;
        top = node;
        size++;
    }

    //Pop up the data at the top of the stack
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("Data in stack is empty~~");
        }
        int val = top.val;
        top = top.next;
        size--;
        return val;
    }

    //Display data in stack
    public void show() {
        if (isEmpty()) {
            System.out.println("The stack is empty.~~");
            return;
        }

        Node temp = top;
        for (int i = 0; i < size; i++) {
            System.out.printf("stack[%d]=%d\n",i,temp.val);
            temp = temp.next;
        }
    }
}


/**
 * struct Node
 */
class Node {

    public int val;
    public Node next;

    public Node() {
    }

    public Node(int val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return "Node{" +
                "val=" + val +
                '}';
    }
}

1.5 stack implementation of integrated calculator

  • Use the stack to implement the comprehensive calculator - Custom priority

  • Train of thought

  • code implementation
/**
 * @author Wnlife
 * @create 2019-10-21 19:03
 * <p>
 * Using the stack of array simulation to realize calculator function (addition, subtraction, multiplication and division)
 */
public class Calculator {


    //Implementation expression evaluation++
    public static void main(String[] args) {

        String expression="7*2*2-5+1-5+3-4";

        //Create two stacks, one is the operand stack and the other is the operator stack
        ArrayStack2 numStack=new ArrayStack2(10);
        ArrayStack2 operStack=new ArrayStack2(10);

        //Define the relevant variables required
        char ch=' ';//Save char from each scan to ch
        String keepNum="";
        int res = 0;//Calculation results

        //Loop out each number and character
        for (int i = 0; i < expression.length(); i++) {
            //Take out each character
            ch = expression.charAt(i);
            //Judge what ch is and deal with it separately
            if(numStack.isOper(ch)){//Operator.
                //Determine whether the current operator stack is empty
                if(operStack.isEmpty()){//If it is empty, directly press the operator stack
                    operStack.push(ch);
                }else {//If the stack is not empty
                    //The priority of the current character is higher than that of the character at the top of the stack. Press it directly into the operator stack.
                    if(operStack.priority(ch)>operStack.priority(operStack.peek())){
                        operStack.push(ch);
                    }else {//If the priority of the current character is less than or equal to the operator at the top of the stack
                        //Take two numbers from the operand stack, and take an operator from the operator stack for calculation.
                        // Put the calculation result into the operand stack, and press the current character into the operand stack
                        int num1 = numStack.pop();
                        int num2 = numStack.pop();
                        int opr = operStack.pop();
                        res = operStack.cal(num1, num2, opr);
                        //Stack the results of the operation
                        numStack.push(res);
                        //Then put the current operator into the symbol stack
                        operStack.push(ch);
                    }
                }
            }else{//The current number of characters
                //Analytical thinking
                //1. When dealing with multi bit numbers, it is not possible to find a number and immediately push it into the stack, because it may be a multi bit number.
                //2. When processing a number, you need to look at the index of the expression and then look at a bit. If it's a number, you need to scan it. If it's a symbol, you need to stack it.
                //3. Therefore, we need to define a variable string for splicing.
                keepNum+=ch;
                //If it's the last bit, go straight to the stack
                if(i==expression.length()-1){
                    numStack.push(Integer.parseInt(keepNum));
                }else{//If it is not the last digit, judge whether the last digit is a number.
                    if(operStack.isOper(expression.charAt(i+1))){
                        //If the next bit is an operator, push the current keepNum directly into the operand stack
                        numStack.push(Integer.parseInt(keepNum));
                        //Important!!!!!! keepNum clear
                        keepNum="";
                    }
                }
            }
        }

        //After the expression scanning is completed, the data is taken out from the operand stack and the operator stack in order for operation, and the final result is put into the operand stack.
        while (!operStack.isEmpty()){
            int num1 = numStack.pop();
            int num2 = numStack.pop();
            int opr = operStack.pop();
            res = operStack.cal(num1, num2, opr);
            //Stack the results of the operation
            numStack.push(res);
        }
        //Operate to pop out the last number of the stack, which is the result.
        int r = numStack.pop();
        System.out.printf("Expression %s = %d",expression,r);

    }

}


/**
 * The stack implemented by array extends the original function
 */
class ArrayStack2 {

    private int maxSize;//Stack size
    private int[] stack;//Array simulation stack, where data is stored
    private int top = -1;//Top of stack

    public ArrayStack2(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }

    //Judge whether the stack is empty
    public boolean isEmpty() {
        return top == -1;
    }

    //Judge whether the stack is full
    public boolean isFull() {
        return top == maxSize - 1;
    }

    //View the value at the top of the stack
    public int peek() {
        return stack[top];
    }

    //Stack -push
    public void push(int num) {

        if (isFull()) {
            System.out.println("The stack is full.~~");
            return;
        }
        stack[++top] = num;
    }

    //Stack -pop
    public int pop() {

        if (isEmpty()) {
            throw new RuntimeException("The stack is empty.~~");
        }
        int val = stack[top];
        top--;
        return val;
    }

    //Display the stack situation [traverse the stack], when traversing, you need to display data from the top of the stack
    public void showStack() {

        if (isEmpty()) {
            System.out.println("Stack empty, no data~~");
            return;
        }
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

    //Returns the priority of the operator. The priority is determined by the programmer. The priority is represented by a number.
    //The larger the number, the higher the priority.
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        }
        return -1;
    }

    //Judge operator
    public boolean isOper(int oper){
        return oper=='+'||oper=='-'||oper=='*'||oper=='/';
    }

    //The method of calculating two numbers
    public int cal(int num1, int num2, int oper) {
        int req = 0;
        switch (oper) {
            case '*':
                req = num2 * num1;
                break;
            case '/':
                req = num2 / num1;
                break;
            case '+':
                req = num2 + num1;
                break;
            case '-':
                req = num2 - num1;
                break;
            default:
                break;
        }
        return req;
    }

}

1.6 prefix, infix, suffix expression (reverse Polish expression)

1.6.1 prefix expression (Polish expression)

1) the prefix expression is also called polish, and the operator of the prefix expression is before the operand.

2) for example, the prefix expression corresponding to (3 + 4) × 5-6 is - × + 3 4 5 6

  • Computer evaluation of prefix expressions

Scan the expression from right to left, when encountering a number, push the number into the stack, when encountering an operator, pop up the two numbers on the top of the stack, use the operator to do the corresponding calculation on them (stack top element and sub top element), and put the result into the stack; repeat the above process until the left end of the expression, and the final calculated value is the result of the expression

For example, the prefix expression corresponding to (3 + 4) × 5-6 is - × + 3 4 5 6. The steps to evaluate the prefix expression are as follows:

1) scan from right to left, press 6, 5, 4, 3 into the stack

2) when the + operator is encountered, pop 3 and 4 (3 is the top element, 4 is the secondary top element), calculate the value of 3 + 4, get 7, and then push 7 into the stack.

3) next is the X operator, so pop up 7 and 5, calculate 7 × 5 = 35, and stack 35.

4) finally, the - Operator calculates the value of 35-6, i.e. 29, to get the final result.

1.6.2 infix expression

1) infix expression is a common operation expression, such as (3 + 4) × 5-6

2) the evaluation of infix expression is the most familiar one to us, but it is not easy for computer to operate (as we can see in the previous case). Therefore, when calculating the result, infix expression is often converted to other expressions for operation (generally converted to suffix expression.)

1.6.3 suffix expression

1) the suffix expression, also known as the reverse Polish expression, is similar to the prefix expression except that the operator is after the operand

2) the suffix expression corresponding to (3 + 4) × 5-6 is 34 + 5 × 6 –

3) another example:

  • Computer evaluation of suffix expressions

Scan the expression from left to right, when encountering numbers, push them into the stack, when encountering operators, pop up the two numbers on the top of the stack, use operators to do corresponding calculations on them (secondary top element and stack top element), and put the results into the stack; repeat the above process until the right end of the expression, and the final calculated value is the result of the expression

For example, the suffix expression corresponding to (3 + 4) × 5-6 is 3 4 + 5 × 6 - - the steps for evaluating the suffix expression are as follows:

1) scan from left to right and press 3 and 4 into the stack;

2) encounter the + operator, so pop up 4 and 3 (4 is the top element, 3 is the secondary top element), calculate the value of 3 + 4, get 7, and then put 7 in the stack;

3) stack 5;

4) next is the X operator, so pop up 5 and 7, calculate 7 × 5 = 35, and stack 35;

5) stack 6;

6) finally, the - Operator calculates the value of 35-6, i.e. 29, to get the final result.

1.6.4 inverse Polish calculator

We have completed an inverse Polish calculator, which requires the following tasks:

1) input an inverse Polish expression (suffix expression), and use stack to calculate the result.

2) it supports parentheses and multi digit integers, because we mainly talk about data structure here, so the calculator is simplified and only supports the calculation of integers.

3) thought analysis

For example, the suffix expression corresponding to (3 + 4) × 5-6 is 3 4 + 5 × 6 - - the steps for evaluating the suffix expression are as follows:

1) scan from left to right and press 3 and 4 into the stack;

2) encounter the + operator, so pop up 4 and 3 (4 is the top element, 3 is the secondary top element), calculate the value of 3 + 4, get 7, and then put 7 in the stack;

3) stack 5;

4) next is the X operator, so pop up 5 and 7, calculate 7 × 5 = 35, and stack 35;

5) stack 6;

6) finally, the - Operator calculates the value of 35-6, i.e. 29, to get the final result.

4) code completion

/**
 * @author Wnlife
 * @create 2019-10-27 19:16
 * <p>
 * Inverse Polish Calculator: find the final value according to the Polish expression entered
 */
public class PolandNotation {

    public static void main(String[] args) {

        //First define to inverse Polish expression
        //(30+4)×5-6  => 30 4 + 5 × 6 - => 164
        // 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +
        //test
        //For convenience, the numbers and symbols of the inverse Polish expression are separated by spaces
        String suffixExpression = "30 4 + 5 * 6 -";
//        String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76

        List<String> list = getListString(suffixExpression);
        int req = calculate(list);
        System.out.println("req = " + req);


    }

    //Put a reverse Polish expression, data and operator in ArrayList
    public static List<String> getListString(String suffixExpression){
        //Split the suffixExpression
        String[] strings = suffixExpression.split(" ");
        List<String> list = Arrays.asList(strings);
        return list;
    }

    //Complete the operation of inverse Polish expression
	/*
	    1)Scan from left to right, press 3 and 4 into the stack;
		2)Encounter + operator, so pop 4 and 3 (4 is the top element of stack, 3 is the secondary top element), calculate the value of 3 + 4, get 7, and then push 7 into the stack;
		3)Stack 5;
		4)Next is the X operator, so pop up 5 and 7, calculate 7 × 5 = 35, and stack 35;
		5)Stack 6;
		6)Finally, the - Operator calculates a value of 35-6, 29, to get the final result
	 */
    public static int calculate(List<String> list) {
        Stack<String>stack=new Stack<>();
        for (String s : list) {
            //If the current data is a number
            if(s.matches("\\d+")){
                stack.push(s);
            }else {//The current element is a symbol
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int req=0;
                switch (s){
                    case "+":
                        req=num1+num2;
                        break;
                    case "-":
                        req=num1-num2;
                        break;
                    case "*":
                        req=num1*num2;
                        break;
                    case "/":
                        req=num1/num2;
                        break;
                    default:
                        throw new RuntimeException("There's something wrong with the symbols");
                }
                stack.push(req+"");
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

1.6.5 infix expression to suffix expression

You can see that the suffix expression is suitable for calculation, but it is not easy for people to write it, especially when the expression is very long. Therefore, in the development, we need to convert the infix expression to the suffix expression.

  • The specific steps are as follows:

1) initialize two stacks: operator stack s1 and stack s2 storing intermediate results;

2) scan infix expression from left to right;

3) when the operands are encountered, press s2;

4) when encountering the operator, compare its priority with s1 stack top operator:

(1) if s1 is empty or the top of stack operator is the left bracket "(", the operator is directly pushed into the stack;

Otherwise, if the priority is higher than the top of stack operator, the operator will also be pressed into s1;

(3) otherwise, pop up the operator at the top of s1 stack and press it into s2, and turn to (4-1) again to compare it with the new operator at the top of s1 stack;

5) when brackets are encountered:
(1) if it is the left bracket "(", press s1 directly.
(2) if it is a right bracket ")", the operators at the top of s1 stack will pop up in turn, and press s2 until the left bracket is encountered. At this time, the pair of brackets will be discarded.

6) repeat steps 2 to 5 until the rightmost side of the expression

7) pop up the remaining operators in s1 and press them into s2

8) pop out the elements in s2 in turn and output them. The reverse order of the result is the suffix expression corresponding to infix expression.

  • For example:

The process of converting infix expression "1 + ((2 + 3) × 4) - 5" to suffix expression is as follows: therefore, the result is "1 23 + 4 × + 5 –"

  • Thought analysis:

  • Code implementation:
/**
 * @author Wnlife
 * @create 2019-10-27 21:05
 * <p>
 * Infix expression - > suffix expression (reverse Polish expression)
 */
public class InfixToSuffixExpression {

    public static void main(String[] args) {
        /*
        Complete the function of converting a infix expression to a suffix expression
        Explain:
        1. 1+((2+3)*4)-5 => To 1 2 3 + 4 * + 5 –
        2. Because it is inconvenient to operate str directly, first set the List corresponding to the expression of "1 + ((2 + 3) * 4) - 5" - > infix.
        That is, "1 + ((2 + 3) × 4) - 5" - > ArrayList [1, +, (, 2, +, 3,), *, 4,), -, 5]
        3. List corresponding to infix expression - > list corresponding to suffix expression
           That is, ArrayList [1, +, (, 2, +, 3,), *, 4,), -, 5] - > ArrayList [1,2,3, +, 4, *, +, 5, –]
        */

        String infixExpression = "1+((2+3)*4)-5";
        List<String> infixExpressionList = InfixToList(infixExpression);
        System.out.println("Infix expression corresponding to list = " + infixExpressionList);//[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
        List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
        System.out.println("Corresponding to suffix expression list = "+suffixExpreesionList);//[1, 2, 3, +, 4, *, +, 5, -]

    }


    //Method: List corresponding to infix expression - > List corresponding to suffix expression
    //That is, ArrayList [1, +, (, 2, +, 3,), *, 4,), -, 5] - > ArrayList [1,2,3, +, 4, *, +, 5, –]
    public static List<String> parseSuffixExpreesionList(List<String>infixList){

        //Initialize two stacks
        Stack<String>s1=new Stack<>();//Representative operator stack
        List<String>s2=new ArrayList<>();//Represents the intermediate result stack (Note: used here)

        for (String s : infixList) {
            if(s.matches("\\d+")){//If it's an operand, push it directly into stack s2
                s2.add(s);
            }else if (s.equals("(")){//If it's left bracket, it's s1.
                s1.push(s);
            }else if (s.equals(")")){//If it is a right parenthesis ")", the operators at the top of s1 stack will pop up in turn, and press s2 until the left parenthesis is encountered. At this time, this pair of parentheses will be discarded.
                while (!s1.isEmpty()&&!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();//Discard left parenthesis
            }else {//If it's an operator
                if(s1.isEmpty()||s1.peek().equals("(")){//If s1 is empty, or the top of stack operator is left bracket "(", the operator will be directly pushed into the stack;
                    s1.push(s);
                }else if (Operation.getPriority(s)>Operation.getPriority(s1.peek())){//Otherwise, if the priority is higher than the stack top operator, the operator will also be pressed into s1;
                    s1.push(s);
                }else {//Otherwise, pop up the operator at the top of s1 stack and press it into s2, and turn to (4-1) again to compare with the new operator at the top of s1 stack;
                    while (!s1.isEmpty()&&Operation.getPriority(s)<=Operation.getPriority(s1.peek())){
                        s2.add(s1.pop());
                    }
                    s1.push(s);//You also need to push s into the stack
                }
            }
        }
        //Pop up the remaining operators in s1 and add s2
        while (!s1.isEmpty()){
            s2.add(s1.pop());

        }
        return s2;//Note that since it is stored in the List, the output in sequence is the List corresponding to the corresponding suffix expression.
    }


    //Convert infix expression to List
    public static List<String> InfixToList(String str) {

        ArrayList<String> list = new ArrayList<>();
        int i = 0;//This is a pointer to traverse the infix expression string
        String keepNum ;// Splicing of multi bit numbers
        char c;// Every time a character is traversed, it is put into c.
        do {
            c = str.charAt(i);
            if (c < 48 || c > 57) {//Indicates that it is currently an operator, directly into the set
                list.add(c + "");
                i++;
            } else {//If it is a number, consider the case of [multi bit number]
                keepNum = "";
                while (i < str.length() && (c = str.charAt(i)) >= 48 && (c = str.charAt(i)) <= 57) {
                    keepNum += c;
                    i++;
                }
                list.add(keepNum);
            }
        } while (i < str.length());
        return list;
    }


}

//Writing an Operation class can return the priority corresponding to an operator
class Operation{

    private static int ADD=1;
    private static int SUB=1;
    private static int MUL=2;
    private static int DIV=2;

    //Returns the priority of the operator
    public static int getPriority(String operation){
        int req=0;
        switch (operation){
            case "+":
                req=ADD;
                break;
            case "-":
                req=SUB;
                break;
            case "*":
                req=MUL;
                break;
            case "/":
                req=DIV;
                break;
            default:
                System.out.println("Nonexistent operator:"+operation);
                break;
        }
        return req;
    }
}

1.6.6 complete version of inverse Polish calculator

Full version of the counter Polish calculator with functions including

1) support + - * / ()

2) multiple digits, supporting decimal.

3) compatible processing, filtering any blank characters, including spaces, tabs, page breaks

/**
 * Full version of the counter Polish calculator with functions including
 * Support + - * / ()
 * Multi digit number, support decimal,
 * Compatible processing, filtering any blank characters, including spaces, tabs, page breaks
 */
public class ReversePolishMultiCalc {

	 /**
     * Match + - * / () operator
     */
    static final String SYMBOL = "\\+|-|\\*|/|\\(|\\)";

    static final String LEFT = "(";
    static final String RIGHT = ")";
    static final String ADD = "+";
    static final String MINUS= "-";
    static final String TIMES = "*";
    static final String DIVISION = "/";

    /**
     * Addition and subtraction +
     */
    static final int LEVEL_01 = 1;
    /**
     * Multiplication and division *
     */
    static final int LEVEL_02 = 2;

    /**
     * brackets
     */
    static final int LEVEL_HIGH = Integer.MAX_VALUE;


    static Stack<String> stack = new Stack<>();
    static List<String> data = Collections.synchronizedList(new ArrayList<String>());

    /**
     * Remove all blanks
     * @param s
     * @return
     */
    public static String replaceAllBlank(String s ){
        // \s + matches any white space characters, including spaces, tabs, page breaks, and so on, equivalent to [\ f\n\r\t\v]
        return s.replaceAll("\\s+","");
    }

    /**
     * Judge whether it is a number int double long float
     * @param s
     * @return
     */
    public static boolean isNumber(String s){
        Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
        return pattern.matcher(s).matches();
    }

    /**
     * Judge operator
     * @param s
     * @return
     */
    public static boolean isSymbol(String s){
        return s.matches(SYMBOL);
    }

    /**
     * Matching operation level
     * @param s
     * @return
     */
    public static int calcLevel(String s){
        if("+".equals(s) || "-".equals(s)){
            return LEVEL_01;
        } else if("*".equals(s) || "/".equals(s)){
            return LEVEL_02;
        }
        return LEVEL_HIGH;
    }

    /**
     * matching
     * @param s
     * @throws Exception
     */
    public static List<String> doMatch (String s) throws Exception{
        if(s == null || "".equals(s.trim())) throw new RuntimeException("data is empty");
        if(!isNumber(s.charAt(0)+"")) throw new RuntimeException("data illeagle,start not with a number");

        s = replaceAllBlank(s);

        String each;
        int start = 0;

        for (int i = 0; i < s.length(); i++) {
            if(isSymbol(s.charAt(i)+"")){
                each = s.charAt(i)+"";
                //Stack is empty, (or operator priority is higher than stack top priority
                if(stack.isEmpty() || LEFT.equals(each)
                        || ((calcLevel(each) > calcLevel(stack.peek())) && calcLevel(each) < LEVEL_HIGH)){
                    stack.push(each);
                }else if( !stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek())){
                    //If the stack is not empty and the operator priority is less than or equal to the stack top priority, the stack will be listed until the stack is empty or (, and finally the operator will be listed.
                    while (!stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek()) ){
                        if(calcLevel(stack.peek()) == LEVEL_HIGH){
                            break;
                        }
                        data.add(stack.pop());
                    }
                    stack.push(each);
                }else if(RIGHT.equals(each)){
                    // )Operator, in turn out of stack and list until empty stack or the first) operator is encountered, at this time) out of stack
                    while (!stack.isEmpty() && LEVEL_HIGH >= calcLevel(stack.peek())){
                        if(LEVEL_HIGH == calcLevel(stack.peek())){
                            stack.pop();
                            break;
                        }
                        data.add(stack.pop());
                    }
                }
                start = i ;    //Location of the previous operator
            }else if( i == s.length()-1 || isSymbol(s.charAt(i+1)+"") ){
                each = start == 0 ? s.substring(start,i+1) : s.substring(start+1,i+1);
                if(isNumber(each)) {
                    data.add(each);
                    continue;
                }
                throw new RuntimeException("data not match number");
            }
        }
        //If there are still elements in the stack, the elements need to be listed out of the stack one by one. You can imagine that the top of the stack left in the stack is /, and the bottom of the stack is +. You should list out of the stack one by one, and you can directly flip the entire stack to add it to the queue.
        Collections.reverse(stack);
        data.addAll(new ArrayList<>(stack));

        System.out.println(data);
        return data;
    }

    /**
     * Work out the result
     * @param list
     * @return
     */
    public static Double doCalc(List<String> list){
        Double d = 0d;
        if(list == null || list.isEmpty()){
            return null;
        }
        if (list.size() == 1){
            System.out.println(list);
            d = Double.valueOf(list.get(0));
            return d;
        }
        ArrayList<String> list1 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            list1.add(list.get(i));
            if(isSymbol(list.get(i))){
                Double d1 = doTheMath(list.get(i - 2), list.get(i - 1), list.get(i));
                list1.remove(i);
                list1.remove(i-1);
                list1.set(i-2,d1+"");
                list1.addAll(list.subList(i+1,list.size()));
                break;
            }
        }
        doCalc(list1);
        return d;
    }

    /**
     * operation
     * @param s1
     * @param s2
     * @param symbol
     * @return
     */
    public static Double doTheMath(String s1,String s2,String symbol){
        Double result ;
        switch (symbol){
            case ADD : result = Double.valueOf(s1) + Double.valueOf(s2); break;
            case MINUS : result = Double.valueOf(s1) - Double.valueOf(s2); break;
            case TIMES : result = Double.valueOf(s1) * Double.valueOf(s2); break;
            case DIVISION : result = Double.valueOf(s1) / Double.valueOf(s2); break;
            default : result = null;
        }
        return result;

    }

    public static void main(String[] args) {
        //String math = "9+(3-1)*3+10/2";
        String math = "12.8 + (2 - 3.55)*4+10/5.0";
        try {
            doCalc(doMatch(math));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

 

 

 

 

Posted by tisource on Sun, 27 Oct 2019 22:20:14 -0700