数据结构和算法--4栈(数组模拟栈,中缀表达式计算器,逆波兰表达式计算器)

1.栈的介绍

*栈stack是一个先入后出的有序列表
*栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表,允许插入和删除的一端为变化的一端,成为栈顶。另一端为固定的一端,成为栈底。
*根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶。而删除元素刚好相反,最先放入的元素最先删除,最后放入的元素最后删除

2.栈的应用场景

1子程序的调用
2处理递归调用
3表达式的转换
4二叉树的遍历
5图形的深度优先搜索法

3.数组模拟栈的思路:

1使用数组来模拟栈
2定义一个top来展示栈顶,初始化为-1
3入栈的操作,当有数据加入到栈时,top++,stack[top] = data
4出栈的操作,int value = stack[top],top--,return value

具体代码

class ArrayStack{
    //最多能存放多少数据
    private int maxSize;
    //数组模拟栈
    private int[] stack;
    //栈顶
    private int top =-1;
    //构造器
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }
    //栈满
    public boolean isFull() {
        return top == maxSize -1;
    }
    //栈空
    public boolean isEmpty() {
        return top == -1;
    }
    //入栈
    public void push(int data) {
        //当有数据加入到栈时,top++,stack[top] = data
        if(isFull()) {
            System.out.println("栈已满,"+data+"不能再放入栈中");
            return;
        }
        top++;
        stack[top] = data;
    }
    //出栈
    public int pop() {
        if(isEmpty()) {
            throw new RuntimeException("栈空,没有数据");
        }
        int value = stack[top];
        top--;
        return value;
    }
    //遍历栈
    public void list() {
        if(isEmpty()) {
            System.out.println("栈空");
            return;
        }
        for(int i = top;i >= 0;i--) {
            System.out.printf("stack[%d]=%d\n",i,stack[i]);
        }
    }
}

4.中缀表达式计算器

思路:

1 通过一个index值来遍历表达式
2 如果扫描的是一个数字,就直接入数栈
3 如果扫描的是一个符号,分情况
3.1 如果此时符号栈为空,就直接入栈
3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或大于等于栈中的操作符,先不入符号栈。需要从数栈中pop出两个数,符号栈中pop出一个符号进行运算。
  得到的结果入数栈。然后把当前的操作符入符号栈
3.3 如果当前操作符的优先级大于栈中的操作符,就直接入符号栈
4 表达式扫描完毕,顺序的从数栈和符号栈中pop出响应的数和符号,并运行
5 最后在数栈中只有一个数字,就是表达式的结果

具体代码

class ArrayStack2{
    //最多能存放多少数据
    private int maxSize;
    //数组模拟栈
    private int[] stack;
    //栈顶
    private int top =-1;
    //构造器
    public ArrayStack2(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }
    //栈满
    public boolean isFull() {
        return top == maxSize -1;
    }
    //栈顶的值
    public int peek() {
        return stack[top];
    }
    //栈空
    public boolean isEmpty() {
        return top == -1;
    }
    //入栈
    public void push(int data) {
        //当有数据加入到栈时,top++,stack[top] = data
        if(isFull()) {
            System.out.println("栈已满,"+data+"不能再放入栈中");
            return;
        }
        top++;
        stack[top] = data;
    }
    //出栈
    public int pop() {
        if(isEmpty()) {
            throw new RuntimeException("栈空,没有数据");
        }
        int value = stack[top];
        top--;
        return value;
    }
    //确定运算符的优先级,用数字来表示优先级的大小
    public int priority(int oper) {
        if(oper == '*' || oper == '/') {
            return 1;
        }else if(oper == '+' || oper =='-') {
            return 0;
        }else {
            return -1;
        }
    }
    //判断输入的字符是否是表达式
    public boolean isOper(char val) {
        if(val == '*' ||val == '/' ||val == '+' ||val == '-' ) {
            return true;
        }else {
            return false;
        }
    }
    //计算方法
    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;
    }
}
public class Calculator {
    public static void main(String[] args) {
        //表达式
        String expression = "20-1*6+2/1";
        //String expression = "70+20*6-4";
        //创建数栈和符号栈
        ArrayStack2 numStack = new ArrayStack2(20);
        ArrayStack2 operStack = new ArrayStack2(20);
        //定义相关变量
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        String keepNum= "";
        char ch = ' ';
        while(true) {
            //获取字符串中的每一个字符
            ch = expression.substring(index, index+1).charAt(0);
            //判断是否是符号
            if(operStack.isOper(ch)) {
                //如果是符号,分情况
                //3.1 如果此时符号栈为空,就直接入栈
                if(operStack.isEmpty()) {
                    operStack.push(ch);
                }else {
                    //3.3 如果当前操作符的优先级大于栈中的操作符,就直接入符号栈
                    if(operStack.priority(ch) > operStack.priority(operStack.peek())) {
                        operStack.push(ch);
                        //3.2果当前的操作符的优先级小于或等于栈中的操作符
                    }else {
                        boolean flag = true;
                        //判断下一个运算符时候也需要先运算再入栈
                        while(flag) {
                            num1 = numStack.pop();
                            num2 = numStack.pop();
                            oper = operStack.pop();
                            res = numStack.cal(num1, num2, oper);
                            numStack.push(res);
                            if(operStack.isEmpty()) {
                                break;
                            }
                            flag = operStack.priority(ch) <= operStack.priority(operStack.peek());
                        }
                        //得到的结果入数栈
                        operStack.push(ch);
                    
                    }
                        
                }
                    
            }else {
                //如果字符是数字
                //有可能数字占多个字符
                keepNum +=ch;
                //如果此时ch已经是最后一位,就直接入栈
                if(index == (expression.length()-1)) {
                    numStack.push(Integer.valueOf(keepNum));
                }else {
                    //如果后一位是运算符
                    if(operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
                        numStack.push(Integer.valueOf(keepNum));
                        keepNum = "";
                    }
                    
                }
            }
            index++;
            if(index >= expression.length()) {
                break;
            }
        }
        //4表达式扫描完毕,顺序的从数栈和符号栈中pop出响应的数和符号,并运行
        while(true) {
            //如果符号栈为空,则计算到最后结果
            if(operStack.isEmpty()) {
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            System.out.println(res);
            numStack.push(res);
        }
        //输出结果
        System.out.printf("表达式%s = %d",expression,numStack.pop());
    }
}

5.前缀表达式和后缀表达式

前缀表达式(波兰表达式)
运算符位于操作数之前
后缀表达式(逆波兰表达式)
运算符位于操作数之后,支持小括号和多位数整数

6.逆波兰表达式计算器

思路:

例如:(3+4)*5-6对应的后缀表达式是3 4 + 5 * 6
步骤:
1从左至右扫描,将3和4压入堆栈
2遇到+运算符,弹出4和3(4为栈顶元素,3次顶元素),计算出3+4的值,将值压入栈
3将5入栈
4*运算符,弹出5和7,计算出7* 5 的值,将值入栈
5将6入栈
6最后是-运算符,计算出35(次顶)-6(栈顶)的值

具体实现

public class PolandNotation {
    public static void main(String[] args) {
        //定义逆波兰表达式
        String suffixExpression = "3 4 + 5 * 6 -";
        List<String> list = getList(suffixExpression);
        int result = calculate(list);
        System.out.println("运算结果为"+result);
    }
    //将后缀表达式放入集合中
    public static List<String> getList(String suffixExpression){
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<>();
        for (String str : split) {
            list.add(str);
        }
        return list;
    }
    //完成逆波兰表达式的运算
    public static int calculate(List<String> list) {
        //创建栈
        Stack<String> stack = new Stack<String>();
        //遍历集合,把表达式中的数字和运算符拿出来
        for (String item : list) {
            //如果集合拿出的是数字,放入栈中
            if(item.matches("\\d+")) {
                stack.push(item);
            }else {
                //如果拿出来的是运算符,进行运算
                //num2表示栈顶的数字
                int num2 = Integer.valueOf(stack.pop());
                //num1表示次栈中的数字
                int num1 = Integer.valueOf(stack.pop());
                int res = 0;
                switch(item){
                    case "+":
                        res = num1 + num2;
                        break;
                    case "-":
                        res = num1 - num2;
                        break;
                    case "*":
                        res = num1 * num2;
                        break;
                    case "/":
                        res = num1 / num2;
                        break;
                    default:
                        break;
                }
                //把得到的数字入栈
                stack.push(""+res);
            }
        }
        //最后留在stack中的数据就是结果
        return Integer.valueOf(stack.pop());
    }
}

7.中缀表达式转后缀表达式

思路:

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

具体代码

1.将中缀表达式转出集合

//将中缀表达式转成List集合
    public static List<String> toInfixExpressionList(String str){
        List<String> list1 = new ArrayList<String>();
        //i作为一个指针,用于遍历中缀表达式字符串
        int i = 0;
        //后面考虑到多位数
        String numbers;
        //表示遍历到的字符
        char c;
        do {
            //如果c不是一个数字,直接加入集合
            if((c = str.charAt(i)) < 48 || (c = str.charAt(i)) > 57){
                list1.add(""+c);
                i++;
            }else {
                //如果c是数字,需要考虑多位数
                numbers = "";
                while(i < str.length() && (c = str.charAt(i)) >= 48 && (c = str.charAt(i)) <= 57) {
                    numbers += c;
                    i++;
                }
                list1.add(numbers);
                
            }
        }while(i < str.length());
    
        return list1;
    }

2.规定优先级

class Operation{
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 1;
    private static int DIV = 1;
    //写一个方法,返回对应的优先级数字
    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:
            break;
        }
        return result;
    }
}

3.中缀表达式的集合转成后缀表达式

//将中缀表达式转成后缀表达式
    public static List<String> parseSuffixExpreList(List<String> list){
        //1 初始化两个栈,运算符s1栈和储存中间结果的s2栈
        //s1为符号栈
        Stack<String> s1 = new Stack<String>();
        //s2为中间结果栈
        List<String> s2 = new ArrayList<String>();
        //2 从左至右扫描中缀表达式
        for (String item : list) {
            //3 遇到操作数时,将其压入s2栈
            if(item.matches("\\d+")) {
                s2.add(item);
                //如果s1为空,或栈顶运算符为左括号( ,则直接将此运算符入s1栈
            }else if(item.equals("(")) {
                s1.push(item);
                //5.2 如果是右括号),则依次弹出s1栈顶的运算符,并压入s2栈内
            }else if(item.equals(")")) {
                //直到遇到左括号为止
                while(!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                //此时将这一对括号丢弃
                s1.pop();
            }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中
                s1.push(item);
            }
            
        }
        //7 将s1中剩余的运算符依次弹出并压入s2栈中
        while(s1.size() != 0) {
            s2.add(s1.pop());
        }
        return s2;
    }

4.测试代码

public static void main(String[] args) {
     String expression = "1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("中缀表达式对应的List"+infixExpressionList);
        List<String> parseSuffixExpreList = parseSuffixExpreList(infixExpressionList);
        System.out.println("后缀表达式对应的List"+parseSuffixExpreList);
        System.out.printf("expression=%d",calculate(parseSuffixExpreList));
    }

猜你喜欢

转载自www.cnblogs.com/bai3535/p/12115373.html