image.png
image.png
image.png

实现栈的思路分析

数组模拟栈

image.png
image.png

  1. //数组实现栈
  2. package com.cheung.stack;
  3. public class ArrayStackDemo {
  4. public static void main(String[] args) {
  5. ArrayStack arrayStack = new ArrayStack(10);
  6. arrayStack.push(2);
  7. arrayStack.push(5);
  8. arrayStack.push(4);
  9. arrayStack.pop();
  10. arrayStack.list();
  11. }
  12. }
  13. //定义一个ArrayStack 表示一个栈
  14. class ArrayStack{
  15. private int maxSize; //栈的大小
  16. private int[] stack; //数组,数组模拟栈,数据就放在该数组中
  17. private int top = -1; //top表示栈顶,初始化为-1
  18. public ArrayStack(int maxSize){
  19. this.maxSize = maxSize;
  20. stack = new int[this.maxSize];
  21. }
  22. //栈满情况
  23. public boolean isFull(){
  24. return top == maxSize - 1;
  25. }
  26. //栈空
  27. public boolean isEmpty(){
  28. return top == -1;
  29. }
  30. //入栈
  31. public void push(int value){
  32. if(isFull()){
  33. System.out.println("Full!");
  34. return;
  35. }
  36. top += 1;
  37. stack[top] = value;
  38. }
  39. //出栈
  40. public int pop(){
  41. if(isEmpty()){
  42. throw new RuntimeException("栈空");
  43. }
  44. int value = stack[top];
  45. top -= 1;
  46. return value;
  47. }
  48. //查看栈内元素情况
  49. public void list(){
  50. if(isEmpty()){
  51. System.out.println("栈空!");
  52. }
  53. for(int i = top; i >= 0; i--){
  54. System.out.printf("stack[%d] = %d\n",i,stack[i]);
  55. }
  56. }
  57. }

栈实现综合计算器 中缀表达式

image.png
image.png

//没有考虑括号问题
package com.cheung.stack;

public class Calculator {
    public static void main(String[] args) {
        //根据前面老师思路,完成表达式的运算
        String expression = "30+2*6-2";
        //创建两个栈,一个数栈,一个符号栈
        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;
        char ch = ' '; //将每次扫描得到的char保存到ch
        String keepNum = ""; //用于拼接多位数

        //开始用while循环扫描字符串
        while(true){
            //依次得到expression的每一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            //判断是数字还是符号 符号比较
            if(numStack.isOper(ch)){ //是不是运算符
                if(!operStack.isEmpty()){
                    //不为空继续比较,分两种情况
                    //当前ch优先级小于等于操作数栈栈顶元素就做运算
                    if(operStack.priority(ch) <= operStack.priority(operStack.peek())){
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1, num2, oper);
                        //操作结果入数栈
                        numStack.push(res);
                        operStack.push(ch);
                    }else {
                        //当前ch优先级大于操作数栈栈顶元素就做入栈
                        operStack.push(ch);
                    }
                }else{
                    //为空直接入栈
                    operStack.push(ch);
                }
            }else {
                //是数字,先判断是不是多位数,判断思路:
                //1 在处理数时,需要向字符串的index下标位置再往后看一位,如果是数字就进行扫描,如果是符号才入栈。
                //2 因此我们定义一个变量字符串,用于拼接
                keepNum += ch;

                //如果ch已经是expression的最后一位,就直接入栈
                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 = ""; //一定要清空!!!!
                }
            }
            //让index + 1,并判断是否扫描到字符串末尾
            index ++;
            if(index >= expression.length()){
                break;
            }

        }

        //当表达式扫描完毕,就顺序从数栈和符号栈中pop出相应的数和符号,并运行。
        while (true){
            //如果符号栈为空,则计算到了最后的结果,数栈中就只有一个数字了
            if(operStack.isEmpty()){
                break;
            }else {
                num1 = numStack.pop();
                num2 = numStack.pop();
                oper = operStack.pop();
                res = numStack.cal(num1, num2, oper);
                //操作结果入数栈
                numStack.push(res);
            }
        }
        System.out.println("运算结果为:" + numStack.pop());
    }
}

//先创建一个栈
//定义一个ArrayStack 表示一个栈
class ArrayStack2{
    private int maxSize; //栈的大小
    private int[] stack; //数组,数组模拟栈,数据就放在该数组中
    private int top = -1; //top表示栈顶,初始化为-1

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

    //栈满情况
    public boolean isFull(){
        return top == maxSize - 1;
    }

    //栈空
    public boolean isEmpty(){
        return top == -1;
    }

    //入栈
    public void push(int value){
        if(isFull()){
            System.out.println("Full!");
            return;
        }
        top += 1;
        stack[top] = value;
    }

    //出栈
    public int pop(){
        if(isEmpty()){
            throw new RuntimeException("栈空");
        }
        int value = stack[top];
        top -= 1;
        return value;
    }

    //查看栈内元素情况
    public void list(){
        if(isEmpty()){
            System.out.println("栈空!");
        }

        for(int i = top; i >= 0; i--){
            System.out.printf("stack[%d] = %d\n",i,stack[i]);
        }
    }

    //返回运算符的优先级,优先级是程序员来定的,优先级使用数字表示
    //数字越大,则优先级就越高
    public int priority(int oper){
        if(oper == '*' || oper == '/'){
            return 1;
        }else if(oper == '+' || oper == '-'){
            return 0;
        }else {
            return -1; //假定目前表达式只有+-*/
        }
    }

    //判断是不是运算符
    public boolean isOper(char val){
        return val == '+' || val == '-' || val == '*' || val == '/';
    }

    //计算方法
    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 int peek(){
        return this.stack[top];
    }
}

前缀表达式(波兰表达式)

image.png
image.png

中缀表达式

image.png

后缀表达式(逆波兰表达式)

image.png
image.png

完成逆波兰计算器

image.png

package com.cheung.stack;

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

public class PolandNotation {
    public static void main(String[] args) {
        //先定义一个逆波兰表达式
        // (3+4)*5-6  ---->  3 4 + 5 * 6 -
        //为了方便,逆波兰表达式的数字和符号使用空格隔开
        String suffixExpression = "3 4 + 5 * 6 -";
        //思路
        //1 先将suffixExpression放到ArrayList中
        //2 将ArrayList传递给一个方法,配合栈完成计算
        List<String> rpnList = getListString(suffixExpression);
//        System.out.println(rpnList);
        System.out.println(calculate(rpnList));
    }

    //将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
    public static List<String> getListString(String str){
        String[] split = str.split(" ");
        ArrayList<String> list = new ArrayList<String>();
        for(String ele: split){
            list.add(ele);
        }
        return list;
    }

   //完成对逆波兰表达式的运算
    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 {
                //弹出两个数,做计算,再入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if("+".equals(item)){
                   res = num1 + num2;
                }else if("-".equals(item)){
                    res = num1 - num2;
                }else if("*".equals(item)){
                    res = num1 * num2;
                }else if("/".equals(item)){
                    res = num1 / num2;
                }else {
                    throw new RuntimeException("运算符有误");
                }
                stack.push(String.valueOf(res));
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

中缀表达式转后缀表达式

image.png
image.png
image.png
image.png

package com.cheung.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. 因为直接对一个字符串进行操作不太方便,因此我们先将字符串转为中缀表达式对应的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(infixExpressionList);
        List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        System.out.println(suffixExpressionList);

        System.out.println("计算结果为" + calculate(suffixExpressionList));


    }
    //方法 将得到的中缀表达式对应的List => 后缀表达式对应的List
    //   即 ArrayList[1,+,(,(,2,+,3,),*,4,),-,5] => ArrayList[1,2,3,+,4,*,+,5,-]
    public static List<String> parseSuffixExpressionList(List<String> ls){
        //定义符号栈
        Stack<String> s1= new Stack<>();
        //说明:因为s2这个栈在整个转换过程中没有pop操作,而且我们还需要逆序输出
        //因此比较麻烦,这里我们用List<String>
//        Stack<String> s2= new Stack<>();
        List<String> s2 = new ArrayList<String>(); //储存中间结果的Lists2

        //遍历ls
        for(String item : ls){
            //如果是一个数,加入s2
            if(item.matches("\\d+")){
                s2.add(item);
            }else if("(".equals(item)){ //如果是一个左括号,也直接入栈
                s1.push(item);
            }else if(")".equals(item)){ //如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
                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.push(item);
            }

        }

        //将s1剩余运算符加入s2中
        while(s1.size() != 0){
            s2.add(s1.pop());
        }

        return s2; //因为是存放在List中,因此按顺序输出就是对应的逆波兰表达式
    }



    //方法:将中缀表达式转为对应的List
    public static List<String> toInfixExpressionList(String s){
        //定义一个List,存放中缀表达式 对应的内容
        List<String> ls = new ArrayList<String>();
        int i = 0;//这是一个指针,用于遍历s
        String str; //对多位数对拼接操作
        char c; //每遍历一个字符,就放入到c
        do{
            //如果c是一个非数字,我们就需要加入到ls
            if((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57){
                ls.add(String.valueOf(c));
                i++;
            }else { //如果是一个数字,需要考虑多位数对问题
                str = ""; //先将str 置空
                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 str){
        String[] split = str.split(" ");
        ArrayList<String> list = new ArrayList<String>();
        for(String ele: split){
            list.add(ele);
        }
        return list;
    }

   //完成对逆波兰表达式的运算
    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 {
                //弹出两个数,做计算,再入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if("+".equals(item)){
                   res = num1 + num2;
                }else if("-".equals(item)){
                    res = num1 - num2;
                }else if("*".equals(item)){
                    res = num1 * num2;
                }else if("/".equals(item)){
                    res = num1 / num2;
                }else {
                    throw new RuntimeException("运算符有误");
                }
                stack.push(String.valueOf(res));
            }
        }
        return Integer.parseInt(stack.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 oper){
        int result = 0;
        switch (oper){
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                System.out.println("奇怪的运算符");
                break;
        }
        return result;
    }
}