Java data structure and algorithm (4) stack

        Recently I learned about the stack and wrote something for later review. It is mainly introduced from seven aspects: (1) Introduction to the stack (2) Logical analysis and array implementation of the stack (3) Using the stack to implement a comprehensive calculator (4) Prefix , Infix, suffix introduction, (5) logic analysis and code implementation of infix to suffix conversion (6) realization of reverse Polish calculator (7) summary

In the previous section, we briefly introduced queues and linked lists, and realized their addition, deletion, modification, and checking through code. If we want to add and delete data through one end, and the other does not change, how should we implement it?

Calculation formula: 7*2*2-5+1-5*3-3=?

How does the bottom layer of the computer get the result? Note that instead of simply listing the calculation, how does the computer understand the calculation (for the computer, what it receives is a string), we are discussing this problem. -->Stack

 

 

(1) Introduction to the stack

          The stack is a linear data structure. You can only store or read data by accessing one of its segments. You can think of the stack as a stack of plates in the restaurant. Put the new plate on the top and remove the plate from the top. Finally, A stacked plate is also the first to be taken away, so the stack is called a first-in-last-out structure (LIFO). Generally speaking, the end that allows operation is called the top of the stack, and the end that is not operable is called the top of the stack. It is the bottom of the stack, and the operation of inserting an element is called Push, and the operation of deleting an element is called Pop. If there are no elements in the stack, it is called an empty stack. The structure of the stack is shown in the figure below:

  1. Stack in English is (stack)
  2. The stack is an ordered list of FILO-First In Last Out.
  3. A stack is a special linear table that restricts the insertion and deletion of elements in the linear table to the same end of the linear table. The end that allows insertion and deletion is the changing end, called the top of the stack (Top), and the other end is the fixed end, called the bottom of the stack (Bottom).
  4. According to the definition of the stack, the first element placed in the stack is at the bottom of the stack, and the last element placed is at the top of the stack, while the deleted element is just the opposite. The last element placed is the first to be deleted, and the first element to be placed is the last.

Take the badminton tube as an example. The badminton tube is a stack. At first, the badminton tube is empty, that is, an empty stack. Then we put the shuttlecock one by one, that is, push into the stack one by one. When we need to use the shuttlecock, from Take it inside the tube, that is, pop out of the stack, but the first badminton we get is the last one we put in.

Implementation of stacking:

Pop operation:

Application scenarios of the stack:

  • Subroutine call: Before jumping to the subroutine, the address of the next instruction will be stored on the stack, and the address will be retrieved after the subroutine is executed to return to the original program.
  • 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 on the stack.
  • Expression conversion [infix expression to postfix expression] and evaluation (actual solution).
  • Traversal of the binary tree. Graphics depth-first (depth-first) search method.

(2) Logical analysis and array realization of the stack

For a stack, the common operations are:

  • isFull()------------Is it full
  • isEmputy()------Check if the stack is empty
  • push()---------Into the stack
  • pop()----------Play
  • list()-------traverse the stack

Logical analysis of the stack:

Array code implementation of the stack:

package JAVADATASTRTUCRE;

import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/19
 * Time: 20:05
 * Description: 栈的数组实现
 */
public class ArrayStatckDemo {
    public static void main(String[] args) {
       //栈的测试
        ArrayStatck arrayStatck = new ArrayStatck(4);
        String key="";
        boolean loop=true;
        Scanner scanner=new Scanner(System.in);
        while (loop){
            System.out.println("show:表示显示栈");
            System.out.println("exit:表示退出程序");
            System.out.println("push:表示添加数据");
            System.out.println("pop:表示出栈");
            key=scanner.next();
            switch (key){
                case "show":
                    arrayStatck.list();
                    break;
                case "push":
                    System.out.println("请输入一个数");
                    int value =scanner.nextInt();
                    arrayStatck.push(value);
                    break;
                case "pop":
                    try {
                        int res=arrayStatck.pop();
                        System.out.println("出栈的数据为"+res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop=false;
                    break;
                default:
                    break;
            }
            System.out.println("程序退出");
         }
    }
}
//定义一个类,表示栈结构
class ArrayStatck{
    private int maxSize;
    private int[] statck;
    private int top=-1;//栈顶为-1

    //构造器
    public ArrayStatck(int maxSize){
        this.maxSize=maxSize;
        statck=new int[maxSize];
    }

    //栈满
     public boolean isFull(){
        return top==maxSize-1;
     }
     //栈空
    public boolean isEmpty(){
        return top==-1;
    }
     //入栈
    public void push(int value){
        //先判断栈是否为满
        if(isFull()){
            System.out.println("栈满");
             return;
        }
      top++;
      statck[top]=value;
    }
   //出栈
    public int pop(){
        //判断栈是否为空
        if(isEmpty()){
            //抛出异常
            throw new RuntimeException("没有数据");
        }
        int value =statck[top];
        top--;
        return value;
    }

    //遍历栈,需要从栈顶开始显示数据
   public void list(){
        if(isEmpty()){
            System.out.println("没有数据!!");
        }
        for(int i=top;i >=0 ; i --){
            System.out.printf("statck[%d]=%d\n",i,statck[i]);
        }
   }
}

operation result:

show:表示显示栈
exit:表示退出程序
push:表示添加数据
pop:表示出栈
push
请输入一个数
12
程序退出
show:表示显示栈
exit:表示退出程序
push:表示添加数据
pop:表示出栈
show
statck[0]=12
程序退出
show:表示显示栈
exit:表示退出程序
push:表示添加数据
pop:表示出栈
pop
出栈的数据为12
程序退出
show:表示显示栈
exit:表示退出程序
push:表示添加数据
pop:表示出栈
show
没有数据!!
程序退出
show:表示显示栈
exit:表示退出程序
push:表示添加数据
pop:表示出栈
exit
程序退出

Process finished with exit code 0

Implementation of stack array and linked list:

logical analysis:

 Code analysis

package DataStrtureDemo;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/20
 * Time: 9:35
 * Description: 堆栈的数组和链表实现
 */
public class LLstatckDemo {
    public static void main(String[] args) {
        //测试代码
     Stack S=new Stack();
     S.push(5);
     S.push(43);
        System.out.println(S.isEmpty());
        System.out.println(S.getSize());
        System.out.println(S.pop());
        LLStack ll=new LLStack();
        System.out.println(ll.isEmpty());
        ll.push(3);
        System.out.println(ll.topEl());

    }
}
//堆栈的链表实现
class LLStack{
    LinkedList list=new LinkedList();
    public LLStack(){

    }
    public void clear(){
        list.clear();
    }
    public boolean  isEmpty(){
        return list.isEmpty();
    }
    public Object topEl(){
        if(isEmpty()){
            throw new RuntimeException("栈为空");
        }
        return list.getLast();
    }
    public Object pop(){
        if(isEmpty()){
            throw new RuntimeException("栈为空");
        }
        return list.removeLast();
    }
    public void push(Object o){
        list.addLast(o);
    }
    public String toString (){
        return list.toString();
    }

}
//堆栈的数组实现
class Stack{
    ArrayList pool=new ArrayList();
    public Stack(){

    }
    public Stack(int n){
        pool.ensureCapacity(n);
    }
 public void clear(){
        pool.clear();
 }
 public boolean isEmpty(){
        return pool.isEmpty();
 }
public int getSize(){
        if(isEmpty()){
            throw new RuntimeException("栈为空");
        }
        return pool.size();
}

public Object pop(){
        if(isEmpty()){
            throw new RuntimeException("栈为空");
        }
        return pool.remove(pool.size()-1);
}
public void push(Object o){
        pool.add(o);
}
public String toString(){
        return pool.toString();
}
}

The results of the operation are:

(3) Use the stack to realize a comprehensive calculator

logical analysis:

Thinking analysis:

 The calculation idea of ​​using the stack to complete the expression

1. Through an index value (index), to traverse our expression

2. If we find that it is a number, we directly enter the stack

3. If it is found that a symbol is scanned, it is divided into the following situations

    3.1 If the current symbol stack is found to be empty, it will be pushed directly into the stack

    3.2 If there are operators in the symbol stack, compare them. If the priority of the current operator is less than or equal to the operators in the stack, you need to pop two numbers from the number stack and pop a symbol from the symbol stack , The operation will get the result, put it into the stack, and then put the current operator into the symbol stack, if the priority of the current operator is greater than the operator in the stack, it will directly enter the symbol stack.

4. When the expression is scanned, pop the corresponding numbers and symbols from the number stack and symbol stack in sequence, and run. 5. Finally, there is only one number on the number stack, which is the result of the expression

Code:

package JAVADATASTRTUCRE;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/20
 * Time: 17:34
 * Description: 利用堆栈实现综合计算器
 */
public class Calculator {
    public static void main(String[] args) {
       String expression="73+2*6-2+6/2";
       //创建栈
        ArrayStatck2 numStack = new ArrayStatck2(10);
        ArrayStatck2 operStack = new ArrayStatck2(10);
        //定义辅助变量
        int index=0;//用于扫描
        int num1=0;
        int num2=0;
        int oper=0;
        int res=0;
        char ch=' ';
        String keepNum="";
        //利用while循环扫描experssion
         while (true){
             //依次得到experssion的每一个字符
             ch=expression.substring(index,index+1).charAt(0);
             //判断ch是什么,然后处理,数字或符号
             if(operStack.isOper(ch)){//是否为运算符
                //判断当前符号栈是否为空
                 if(!operStack.isEmpty()){
                     //处理
                     if(operStack.priority(ch)<=operStack.priority(operStack.peak())){
                         num1=numStack.pop();
                         num2=numStack.pop();
                         oper=operStack.pop();
                         res=numStack.cal(num1,num2,oper);
                         numStack.push(res);
                         operStack.push(ch);

                     }else {
                         operStack.push(ch);
                     }
                 }else{
                     operStack.push(ch);
                 }
             }else{
             //     numStack.push(ch-48);//表达式中的数据为'1',需要减去48变成1,详见ascl
                 //处理多位数
                 keepNum+=ch;
                 //如果ch为表达式的最后一位,则直接入栈

                 if(index==expression.length()-1){
                     numStack.push(Integer.parseInt(keepNum));
                 }else {

                     //判断下一位是否为数字,若为数字,继续扫描,若为
                     //运算符,入数栈
                     if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                         //如果后一位是操作符,则入栈
                         numStack.push(Integer.parseInt(keepNum));
                         //keepNum清空
                         keepNum = "";
                     }
                 }

             }
             index++;
             //判断是否扫描到最后
             if(index>=expression.length()){
                 break;
             }

         }
         //计算结果
         while (true){
             //如果符号栈为空,则数栈中只有一个结果
             if(operStack.isEmpty()){
                 break;
             }
             num1=numStack.pop();
             num2=numStack.pop();
             oper=operStack.pop();
             res=numStack.cal(num1,num2,oper);
             numStack.push(res);

         }
        System.out.println("表达式"+expression+"的运行结果为:"+numStack.pop());

    }
}



//创建一个栈
class ArrayStatck2{
    private int maxSize;//栈大小
    private int[] statck;
    private int top=-1;//栈顶为-1

    //构造器
    public ArrayStatck2(int maxSize){
        this.maxSize=maxSize;
        statck=new int[maxSize];
    }

    //栈满
    public boolean isFull(){
        return top==maxSize-1;
    }
    //栈空
    public boolean isEmpty(){
        return top==-1;
    }
    //入栈
    public void push(int value){
        //先判断栈是否为满
        if(isFull()){
            System.out.println("栈满");
            return;
        }
        top++;
        statck[top]=value;
    }
    //出栈
    public int pop(){
        //判断栈是否为空
        if(isEmpty()){
            //抛出异常
            throw new RuntimeException("没有数据");
        }
        int value =statck[top];
        top--;
        return value;
    }

    //查看栈顶的值
    public int peak(){
        return statck[top];
    }


    //遍历栈,需要从栈顶开始显示数据
    public void list(){
        if(isEmpty()){
            System.out.println("没有数据!!");
        }
        for(int i=top;i >=0 ; i --){
            System.out.printf("statck[%d]=%d\n",i,statck[i]);
        }
    }

    //返回运算符的优先级
    //优先级使用数字表示,则优先级越高
    public int priority(int oper){
        if(oper=='*'||oper=='/'){
            return 1;
        }else if (oper=='+'||oper=='-'){
            return 0;
        }else{
            return -1;
        }
    }
    //判断是否为运算符
     public boolean isOper(char value){
        return value=='*'|| value=='+'|| value=='-'||value=='/';
     }
     //计算方法
    public int cal(int num1,int num2,int oper){
        int res=0;//存储计算结果
        switch (oper){
            case '+':
                res=num1+num2;
                break;
            case'-':
                res=num2-num1;
                break;
            case'*':
                res=num1*num2;
                break;
            case '/':
                res=num2/num1;
                break;
            default:
                break;
        }
        return res;

    }
}

The results of the operation are:

Calculation 73+2*6-2+6/2

(4) Brief introduction of prefix, infix, suffix expression

     Prefix expression refers to the fact that no parentheses are included. The operator is placed in front of the two operands, strictly from right to left (no longer consider the precedence rules of operators), and all calculations are based on the order in which the operators appear.

    For example: (3+4)×5-6 corresponds to the prefix expression-× + 3 4 5 6.

    Computer calculation of prefix expression:

           Scan the expression from right to left. When a number is encountered, the number is pushed onto the stack. When an operator is encountered, the two numbers on the top of the stack are popped, and the operators are used to perform corresponding calculations (stack top element and next top element) ) And push the result into the stack; repeat the above process until the leftmost end of the expression, and the final value obtained by the operation is the result of the expression

For example: (3+4)×5-6 corresponds to the prefix expression-× + 3 4 5 6, the evaluation steps for the prefix expression are as follows:

  1. Scan from right to left, push 6, 5, 4, 3 onto the stack
  2. Encountered the + operator, so pop 3 and 4 (3 is the top element on the stack, 4 is the next top element), calculate the value of 3+4, get 7, and then push 7 onto the stack
  3. Next is the × operator, so 7 and 5 are popped out, 7×5=35 is calculated, and 35 is pushed onto the stack
  4. Finally is the-operator, which calculates the value of 35-6, which is 29, which gives the final result

Infix expression:

   Infix expressions are common operation expressions, such as (3+4)×5-6 The evaluation of infix expressions is the most familiar to us. 

   But it is not easy to operate for the computer (this problem can be seen in the previous case), therefore, when calculating the result, the infix expression is often converted into other expressions for operation (usually converted into suffix expression formula.)

Suffix expression:

   Postfix expressions are also called reverse Polish expressions, similar to prefix expressions, except that the operator is located after the operand, which means that parentheses are not included. The operator is placed after the two operands, and all calculations appear as operators. The order is strictly from left to right (no longer consider the precedence rules of operators).

   For example: (3+4)×5-6 The corresponding suffix expression is 3 4 + 5 × 6 –

Normal expression

Inverse Polish expression

a+b

a b +

a+(b-c)

a b c - +

a+(b-c)*d

a b c – d * +

a+d*(b-c)

a d b c - * +

a=1+3

a 1 3 + =

Computer calculation of suffix expression:

           Scan the expression from left to right. When you encounter a number, push the number onto the stack. When you encounter an operator, pop the two numbers on the top of the stack, and use the operator to do the corresponding calculations on them (the next top element and the top element of the stack) ), and push the result to the stack; repeat the above process until the rightmost end of the expression, the final value obtained by the operation is the result of the expression

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

  1. Scan from left to right, push 3 and 4 onto the stack;
  2. Encounter the + operator, so pop 4 and 3 (4 is the top element on the stack, 3 is the next top element), calculate the value of 3+4, get 7, and then push 7 onto the stack;
  3. Put 5 on the stack; Next is the × operator, so pop 5 and 7, calculate 7×5=35, and put 35 on the stack;
  4. Put 6 into the stack; the last is the-operator, which calculates the value of 35-6, which is 29, and the final result is obtained    

The infix expression is transformed into a prefix expression:

Convert an infix expression to a prefix expression:
follow the steps below:
(1) Initialize two stacks: the operator stack S1 and the stack S2 that stores intermediate results;
(2) Scan the infix expression from right to left;
(3) ) When encountering an operand, push it into S2;
(4) When encountering an operator, compare it with the priority of the S1 stack top operator:
(4-1) If S1 is empty, or the stack top operator is Right parenthesis ")", then this operator is directly put on the stack;
(4-2) Otherwise, if the priority is higher or equal to the operator on the top of the stack, the operator is also pushed into S1;
(4-3) Otherwise , Pop the operator at the top of the stack of S1 and push it into S2, and go to (4-1) again to compare with the new operator at the top of the stack in S1;
(5) When brackets are encountered:
(5-1) If If it is the right parenthesis ")", then directly press S1;
(5-2) If it is the left parenthesis "(", then pop up the operators on the top of the S1 stack in turn, and press S2 until the right parenthesis is encountered, at this time Discard this pair of parentheses;
(6) Repeat steps (2) to (5) until the leftmost of the expression;
(7) Pop up the remaining operators in S1 and press S2;
(8) Pop up S2 in turn The elements in and output, and the result is the prefix expression corresponding to the infix expression.
For example, the process of converting the infix expression "1+((2+3)×4)-5" into a prefix expression is as follows: 

Infix expression is converted to prefix expression, postfix expression

(5) Logic analysis and code realization of infix expression to suffix expression

    Logical analysis of infix expression to postfix expression:

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

2) Scan the infix expression from left to right;

3) When it encounters the operand, press it to s2;

4) When encountering an operator, compare its priority with the operator on the top of the s1 stack:

    1. If s1 is empty, or the operator on the top of the stack is a left bracket "(", then this operator is directly put on the stack;

    2. Otherwise, if the priority is higher than that of the operator on the top of the stack, the operator is also pushed into s1;

   3. Otherwise, pop the operator at the top of the s1 stack and push it into s2, and go to (4.1) again to compare with the new operator at the top of the stack in s1;

5) When encountering parentheses:

      (1) If it is the left parenthesis "(", then directly press s1 (2) If it is the right parenthesis ")", the operators on the top of the s1 stack will be popped in turn, and s2 will be pressed until the left parenthesis is encountered. Discard this pair of parentheses when

6) Repeat steps 2 to 5 until the rightmost of the expression

7) Pop up the remaining operators in s1 and push them into s2

8) Pop up and output the elements in s2 in turn, the reverse order of the result is the postfix expression corresponding to the infix expression

Example: The process of converting the infix expression "1+((2+3)×4)-5" into a postfix expression is as follows, so the result is "1 2 3 + 4 × + 5 –"

Code:

package JAVADATASTRTUCRE;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/23
 * Time: 16:00
 * Description: 中缀转后缀的测试
 */
public class NotationTest {
    public static void main(String[] args) {
        //中缀表达式转化为后缀表达式
        //1+(2+3)*4-5   =>1 2 3 + 4 * + 5 -
        String expression="1+(2+3)*4-5";
        System.out.println("中缀表达式为"+expression);
        List<String> infixExpressionList =toFixExpressionList(expression);
        System.out.println("数组中的表达式为"+infixExpressionList);
        List<String> parseSufferExpression= parseSuffixExperssionList(infixExpressionList);
        System.out.println("后缀表达式为:"+parseSufferExpression);


        //定义一个逆波兰表达式
       // String suffiixExpression = "4 5 * 8 - 60 + 8 2 / +";
        //先将"3 4 + 5 * 6 -" 放入ArrayList中,
        //将ArrayList  传递一个方法,遍历ArrayList配合栈完成计算
         // List<String> s=getListString(suffiixExpression);
        //System.out.println(s);
        int res=calculate(parseSufferExpression);
        System.out.println("输出的结果为"+res);
    }
    //将波兰表表达式依次将数据保存到数组中
    public static List<String> getListString(String suffixExpression){
        //j将suffisExression分割
        String []split=suffixExpression.split(" ");
        List<String> list =new ArrayList<String>();
        for(String ele:split){
            list.add(ele);

        }
        return list;
    }
    //将中缀表达式转化为后缀表达式
    //s="1+(2+3)*4-5"
    public static  List<String>  toFixExpressionList(String s){
        //定义一个List,存放中缀表达式
        List<String> ls=new ArrayList<>();
        int i=0;//定义一个指针,用于遍历中缀表达式
        String str;//多位数的拼接
        char c;
        do {
            //若c非数字,则放入ls(可能为字符或其他)
            //ascll中  数字介于48与57之间
            if((c=s.charAt(i))<48||(c=s.charAt(i))<57){
                ls.add(""+c);
                i++;
            }else{//若为数,考虑多位数   0[48] -->9[57]
                str="";
                while(i<s.length()&&(c=s.charAt(i))>=48&&(c=s.charAt(i))<=57){
                    str  +=c;//拼接
                    i++;
                }
            }
        }while (i<s.length());
    return ls;
    }

    public static List<String> parseSuffixExperssionList(List<String> ls){

        //定义两个栈
       Stack<String> s1=new Stack<String>();
       List<String> s2=new ArrayList<String>();

       //遍历ls
        for(String item:ls){
            //加入s2
           //数字匹配
            if(item.matches("\\d+")){
                s2.add(item);
            }else if(item.equals("(")){
                s1.push(item);
            }else if(item.equals(")")){
                //右括号
                while(!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();//弹出(,消除括号
            }else{
                while (s1.size()!=0 && Operation.getValue(s1.peek())  >=Operation.getValue(item)){
                      s2.add(s1.pop());
                }
                //将item压入栈中
                s1.push(item);

            }
        }

       //将S1中的数据加入到是s2中
        while (s1.size()!=0){
            s2.add(s1.pop());
        }
      //顺序输出,即为后缀表达式
        return s2;
    }


//完成逆波兰表达式
   public static int  calculate(List<String> ls){
       Stack<String> statck=new Stack<String>();
       //遍历ls
     for(String item:ls){
         //利用正则表达式取数
         if(item.matches("\\d+")){//匹配多位数
             //入栈
             statck.push(item);
         }else{
             //pop两个数,并运算,在入栈
             int num1=Integer.parseInt(statck.pop());
             int num2=Integer.parseInt(statck.pop());
             int res=0;
             if(item.equals("+")){
                 res=num1+num2;
             }else if(item.equals("-")){//后出栈减去先出栈
                 res=num2-num1;
             }else if(item.equals("*")){
                 res=num1*num2;
             }else if(item.equals("/")){
                 res=num2/num1;
             }else{
                 throw new RuntimeException("运算符有误");
             }
                 //将res入栈
                 statck.push(""+res);
         }

     }
         //将栈中的结果返回
         return Integer.parseInt(statck.pop());
    }
}

//比较有限集
class Operation{
    private  static int ADD=1;
    private  static int SUB=1;
    private  static int MUL=2;
    private  static int DIV=2;

    public static int getValue(String operation){
        int result=0;
        switch(operation){
            case "+":
                result=ADD;
                break;
            case "-":
                result=SUB;
                break;
            case "*":
                result=MUL;
                break;
            case  "/":
                result=DIV;
                break;
            default:
                System.out.println("运算符不存在");
                break;
        }
        return result;
    }

}

The results of the operation are:

Another way to implement infix to suffix:

package JAVADATASTRTUCRE;

import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/28
 * Time: 20:28
 * Description: 中缀表达式转化为后缀表达式
 */
public class testInfixToSuffix {
    public static void main(String[] args) {
        String input;
        System.out.println("Enter infix:");
        Scanner scanner = new Scanner(System.in);
        input = scanner.nextLine();
        InfixToSuffix in = new InfixToSuffix(input);
        MyCharStack my = in.doTrans();
        my.displayStack();
    }
}
class InfixToSuffix {
    private MyCharStack s1;//定义运算符栈
    private MyCharStack s2;//定义存储结果栈
    private String input;

    //默认构造方法,参数为输入的中缀表达式
    public InfixToSuffix(String in){
        input = in;
        s1 = new MyCharStack(input.length());
        s2 = new MyCharStack(input.length());
    }
    //中缀表达式转换为后缀表达式,将结果存储在栈中返回,逆序显示即后缀表达式
    public MyCharStack doTrans(){
        for(int j = 0 ; j < input.length() ; j++){
            System.out.print("s1栈元素为:");
            s1.displayStack();
            System.out.print("s2栈元素为:");
            s2.displayStack();
            char ch = input.charAt(j);
            System.out.println("当前解析的字符:"+ch);
            switch (ch) {
                case '+':
                case '-':
                    gotOper(ch,1);
                    break;
                case '*':
                case '/':
                    gotOper(ch,2);
                    break;
                case '(':
                    s1.push(ch);//如果当前字符是'(',则将其入栈
                    break;
                case ')':
                    gotParen(ch);
                    break;
                default:
                    //1、如果当前解析的字符是操作数,则直接压入s2
                    //2、
                    s2.push(ch);
                    break;
            }//end switch
        }//end for

        while(!s1.isEmpty()){
            s2.push(s1.pop());
        }
        return s2;
    }

    public void gotOper(char opThis,int prec1){
        while(!s1.isEmpty()){
            char opTop = s1.pop();
            if(opTop == '('){//如果栈顶是'(',直接将操作符压入s1
                s1.push(opTop);
                break;
            }else{
                int prec2;
                if(opTop == '+' || opTop == '-'){
                    prec2 = 1;
                }else{
                    prec2 = 2;
                }
                if(prec2 < prec1){//如果当前运算符比s1栈顶运算符优先级高,则将运算符压入s1
                    s1.push(opTop);
                    break;
                }else{//如果当前运算符与栈顶运算符相同或者小于优先级别,那么将S1栈顶的运算符弹出并压入到S2中
                    //并且要再次再次转到while循环中与 s1 中新的栈顶运算符相比较;
                    s2.push(opTop);
                }
            }

        }//end while
        //如果s1为空,则直接将当前解析的运算符压入s1
        s1.push(opThis);
    }

    //当前字符是 ')' 时,如果栈顶是'(',则将这一对括号丢弃,否则依次弹出s1栈顶的字符,压入s2,直到遇到'('
    public void gotParen(char ch){
        while(!s1.isEmpty()){
            char chx = s1.pop();
            if(chx == '('){
                break;
            }else{
                s2.push(chx);
            }
        }
    }

}
//自定义一个栈
class MyCharStack {
    private char[] array;
    private int maxSize;
    private int top;

    public MyCharStack(int size){
        this.maxSize = size;
        array = new char[size];
        top = -1;
    }

    //压入数据
    public void push(char value){
        if(top < maxSize-1){
            array[++top] = value;
        }
    }

    //弹出栈顶数据
    public char pop(){
        return array[top--];
    }

    //访问栈顶数据
    public char peek(){
        return array[top];
    }

    //查看指定位置的元素
    public char peekN(int n){
        return array[n];
    }

    //为了便于后面分解展示栈中的内容,我们增加了一个遍历栈的方法(实际上栈只能访问栈顶元素的)
    public void displayStack(){
        System.out.print("Stack(bottom-->top):");
        for(int i = 0 ; i < top+1; i++){
            System.out.print(peekN(i));
            System.out.print(' ');
        }
        System.out.println("");
    }

    //判断栈是否为空
    public boolean isEmpty(){
        return (top == -1);
    }

    //判断栈是否满了
    public boolean isFull(){
        return (top == maxSize-1);
    }

}

The results of the operation are:

Enter infix:
A*(B+C)-D/(E+F)
s1栈元素为:Stack(bottom-->top):
s2栈元素为:Stack(bottom-->top):
当前解析的字符:A
s1栈元素为:Stack(bottom-->top):
s2栈元素为:Stack(bottom-->top):A 
当前解析的字符:*
s1栈元素为:Stack(bottom-->top):* 
s2栈元素为:Stack(bottom-->top):A 
当前解析的字符:(
s1栈元素为:Stack(bottom-->top):* ( 
s2栈元素为:Stack(bottom-->top):A 
当前解析的字符:B
s1栈元素为:Stack(bottom-->top):* ( 
s2栈元素为:Stack(bottom-->top):A B 
当前解析的字符:+
s1栈元素为:Stack(bottom-->top):* ( + 
s2栈元素为:Stack(bottom-->top):A B 
当前解析的字符:C
s1栈元素为:Stack(bottom-->top):* ( + 
s2栈元素为:Stack(bottom-->top):A B C 
当前解析的字符:)
s1栈元素为:Stack(bottom-->top):* 
s2栈元素为:Stack(bottom-->top):A B C + 
当前解析的字符:-
s1栈元素为:Stack(bottom-->top):- 
s2栈元素为:Stack(bottom-->top):A B C + * 
当前解析的字符:D
s1栈元素为:Stack(bottom-->top):- 
s2栈元素为:Stack(bottom-->top):A B C + * D 
当前解析的字符:/
s1栈元素为:Stack(bottom-->top):- / 
s2栈元素为:Stack(bottom-->top):A B C + * D 
当前解析的字符:(
s1栈元素为:Stack(bottom-->top):- / ( 
s2栈元素为:Stack(bottom-->top):A B C + * D 
当前解析的字符:E
s1栈元素为:Stack(bottom-->top):- / ( 
s2栈元素为:Stack(bottom-->top):A B C + * D E 
当前解析的字符:+
s1栈元素为:Stack(bottom-->top):- / ( + 
s2栈元素为:Stack(bottom-->top):A B C + * D E 
当前解析的字符:F
s1栈元素为:Stack(bottom-->top):- / ( + 
s2栈元素为:Stack(bottom-->top):A B C + * D E F 
当前解析的字符:)
Stack(bottom-->top):A B C + * D E F + / - 

Process finished with exit code 0

Result analysis:

Calculation of suffix expression:

Code:

package JAVADATASTRTUCRE;

/**
 * Created with IntelliJ IDEA.
 * User:  yongping Li
 * Date: 2020/11/28
 * Time: 20:44
 * Description: 后缀表达式的计算
 */
public class CalSuffix {
    private MyIntStack1 stack;
    private String input;

    public CalSuffix(String input){
        this.input = input;
        stack = new MyIntStack1(input.length());

    }

    public int doCalc(){
        int num1,num2,result;
        for(int i = 0 ; i < input.length() ; i++){
            char c = input.charAt(i);
            if(c >= '0' && c <= '9'){
                stack.push((char) (c-'0'));//如果是数字,直接压入栈中
            }else{
                num2 = stack.pop();//注意先出来的为第二个操作数
                num1 = stack.pop();
                switch (c) {
                    case '+':
                        result = num1+num2;
                        break;
                    case '-':
                        result = num1-num2;
                        break;
                    case '*':
                        result = num1*num2;
                        break;
                    case '/':
                        result = num1/num2;
                        break;
                    default:
                        result = 0;
                        break;
                }//end switch

                stack.push((char) result);
            }//end else
        }//end for
        result = stack.pop();
        return result;
    }

    public static void main(String[] args) {
        //中缀表达式:1*(2+3)-5/(2+3) = 4
        //后缀表达式:123+*123+/-
        CalSuffix cs = new CalSuffix("123+*523+/-");
        System.out.println(cs.doCalc()); //4
    }

}
//自定义一个栈
class MyIntStack1 {
    private char[] array;
    private int maxSize;
    private int top;

    public MyIntStack1(int size){
        this.maxSize = size;
        array = new char[size];
        top = -1;
    }

    //压入数据
    public void push(char value){
        if(top < maxSize-1){
            array[++top] = value;
        }
    }

    //弹出栈顶数据
    public char pop(){
        return array[top--];
    }

    //访问栈顶数据
    public char peek(){
        return array[top];
    }

    //查看指定位置的元素
    public char peekN(int n){
        return array[n];
    }

    //为了便于后面分解展示栈中的内容,我们增加了一个遍历栈的方法(实际上栈只能访问栈顶元素的)
    public void displayStack(){
        System.out.print("Stack(bottom-->top):");
        for(int i = 0 ; i < top+1; i++){
            System.out.print(peekN(i));
            System.out.print(' ');
        }
        System.out.println("");
    }

    //判断栈是否为空
    public boolean isEmpty(){
        return (top == -1);
    }

    //判断栈是否满了
    public boolean isFull(){
        return (top == maxSize-1);
    }

}

The results of the operation are:

(6) Realization of Inverse Polish Calculator

package com.atguigu.stack;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PolandNotation {

	public static void main(String[] args) {
		
		
		//完成将一个中缀表达式转成后缀表达式的功能
		//说明
		//1. 1+((2+3)×4)-5 => 转成  1 2 3 + 4 × + 5 –
		//2. 因为直接对str 进行操作,不方便,因此 先将  "1+((2+3)×4)-5" =》 中缀的表达式对应的List
		//   即 "1+((2+3)×4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
		//3. 将得到的中缀表达式对应的List => 后缀表达式对应的List
		//   即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]
		
		String expression = "1+((2+3)*4)-5";//注意表达式 
		List<String> infixExpressionList = toInfixExpressionList(expression);
		System.out.println("中缀表达式对应的List=" + infixExpressionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
		List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
		System.out.println("后缀表达式对应的List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–] 
		
		System.out.printf("expression=%d", calculate(suffixExpreesionList)); // ?
		
		
		
		/*
		
		//先定义给逆波兰表达式
		//(30+4)×5-6  => 30 4 + 5 × 6 - => 164
		// 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / + 
		//测试 
		//说明为了方便,逆波兰表达式 的数字和符号使用空格隔开
		//String suffixExpression = "30 4 + 5 * 6 -";
		String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76
		//思路
		//1. 先将 "3 4 + 5 × 6 - " => 放到ArrayList中
		//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算
		
		List<String> list = getListString(suffixExpression);
		System.out.println("rpnList=" + list);
		int res = calculate(list);
		System.out.println("计算的结果是=" + res);
		
		*/
	}
	
	
	
	//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]
	//方法:将得到的中缀表达式对应的List => 后缀表达式对应的List
	public static List<String> parseSuffixExpreesionList(List<String> ls) {
		//定义两个栈
		Stack<String> s1 = new Stack<String>(); // 符号栈
		//说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出
		//因此比较麻烦,这里我们就不用 Stack<String> 直接使用 List<String> s2
		//Stack<String> s2 = new Stack<String>(); // 储存中间结果的栈s2
		List<String> s2 = new ArrayList<String>(); // 储存中间结果的Lists2
		
		//遍历ls
		for(String item: ls) {
			//如果是一个数,加入s2
			if(item.matches("\\d+")) {
				s2.add(item);
			} else if (item.equals("(")) {
				s1.push(item);
			} else if (item.equals(")")) {
				//如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
				while(!s1.peek().equals("(")) {
					s2.add(s1.pop());
				}
				s1.pop();//!!! 将 ( 弹出 s1栈, 消除小括号
			} else {
				//当item的优先级小于等于s1栈顶运算符, 将s1栈顶的运算符弹出并加入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较
				//问题:我们缺少一个比较优先级高低的方法
				while(s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item) ) {
					s2.add(s1.pop());
				}
				//还需要将item压入栈
				s1.push(item);
			}
		}
		
		//将s1中剩余的运算符依次弹出并加入s2
		while(s1.size() != 0) {
			s2.add(s1.pop());
		}

		return s2; //注意因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List
		
	}
	
	//方法:将 中缀表达式转成对应的List
	//  s="1+((2+3)×4)-5";
	public static List<String> toInfixExpressionList(String s) {
		//定义一个List,存放中缀表达式 对应的内容
		List<String> ls = new ArrayList<String>();
		int i = 0; //这时是一个指针,用于遍历 中缀表达式字符串
		String str; // 对多位数的拼接
		char c; // 每遍历到一个字符,就放入到c
		do {
			//如果c是一个非数字,我需要加入到ls
			if((c=s.charAt(i)) < 48 ||  (c=s.charAt(i)) > 57) {
				ls.add("" + c);
				i++; //i需要后移
			} else { //如果是一个数,需要考虑多位数
				str = ""; //先将str 置成"" '0'[48]->'9'[57]
				while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) {
					str += c;//拼接
					i++;
				}
				ls.add(str);
			}
		}while(i < s.length());
		return ls;//返回
	}
	
	//将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList中
	public static List<String> getListString(String suffixExpression) {
		//将 suffixExpression 分割
		String[] split = suffixExpression.split(" ");
		List<String> list = new ArrayList<String>();
		for(String ele: split) {
			list.add(ele);
		}
		return list;
		
	}
	
	//完成对逆波兰表达式的运算
	/*
	 * 1)从左至右扫描,将3和4压入堆栈;
		2)遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
		3)将5入栈;
		4)接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
		5)将6入栈;
		6)最后是-运算符,计算出35-6的值,即29,由此得出最终结果
	 */
	
	public static int calculate(List<String> ls) {
		// 创建给栈, 只需要一个栈即可
		Stack<String> stack = new Stack<String>();
		// 遍历 ls
		for (String item : ls) {
			// 这里使用正则表达式来取出数
			if (item.matches("\\d+")) { // 匹配的是多位数
				// 入栈
				stack.push(item);
			} else {
				// pop出两个数,并运算, 再入栈
				int num2 = Integer.parseInt(stack.pop());
				int num1 = Integer.parseInt(stack.pop());
				int res = 0;
				if (item.equals("+")) {
					res = num1 + num2;
				} else if (item.equals("-")) {
					res = num1 - num2;
				} else if (item.equals("*")) {
					res = num1 * num2;
				} else if (item.equals("/")) {
					res = num1 / num2;
				} else {
					throw new RuntimeException("运算符有误");
				}
				//把res 入栈
				stack.push("" + res);
			}
			
		}
		//最后留在stack中的数据是运算结果
		return Integer.parseInt(stack.pop());
	}

}

//编写一个类 Operation 可以返回一个运算符 对应的优先级
class Operation {
	private static int ADD = 1;
	private static int SUB = 1;
	private static int MUL = 2;
	private static int DIV = 2;
	
	//写一个方法,返回对应的优先级数字
	public static int getValue(String operation) {
		int result = 0;
		switch (operation) {
		case "+":
			result = ADD;
			break;
		case "-":
			result = SUB;
			break;
		case "*":
			result = MUL;
			break;
		case "/":
			result = DIV;
			break;
		default:
			System.out.println("不存在该运算符" + operation);
			break;
		}
		return result;
	}
	
}

 

(7) Summary

       The stack is a simple data structure, and the stack is pushed into and out of the stack through a port. Such a first-in-last-out data structure can be used for reverse printing of data, and a comprehensive computer can be realized.

 

 

 

 

Guess you like

Origin blog.csdn.net/weixin_41792162/article/details/110277189