数据结构 - 栈(Stack)

栈的介绍

  • 栈的英文为(stack)。
  • 栈是一个先入后出(FILO : First In Last Out)的有序列表。
  • 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊的线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)
  • 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶;而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

入栈(push)示意图


在这里插入图片描述


出栈(pop)示意图
在这里插入图片描述

用数组模拟栈

思路分析
在这里插入图片描述

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

代码实现

/**
 * @ClassName ArrayStackDemo
 * @author: shouanzh
 * @Description 数组模拟栈
 * @date 2022/4/30 15:57
 */
public class ArrayStackDemo {

    public static void main(String[] args) {
        // 测试
        // 先创建一个ArrayStack ——> 表示栈
        ArrayStack stack = new ArrayStack(4);
        String key = "";
        // 控制是否退出菜单
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);
        System.out.println("show:表示显示栈");
        System.out.println("exit:表示退出程序");
        System.out.println("push:表示添加数据到栈(入栈)");
        System.out.println("pop:表示从栈取出数据(出栈)");
        while (loop) {
            System.out.println("请输入你的选择:");
            key = scanner.next();
            switch (key) {
                case "show":
                    stack.list();
                    break;
                case "push":
                    System.out.println("请输入一个数字:");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res = stack.pop();
                        System.out.printf("出栈的数据是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出~~");
    }
}


/**
 * 定义一个ArrayStack表示栈
 */
class ArrayStack {

    // 栈的大小
    private int maxSize;

    // 数组模拟栈,数据就放在该数组中
    private int[] stack;

    // top表示栈顶,初始化为-1
    private int top = -1;

    // 构造器 初始化栈的大小
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }


    /**
     * 判断栈满
     * @return boolean
     */
    public boolean isFull() {
        return top == maxSize - 1;
    }

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

    /**
     * 入栈
     * @param value 数据
     */
    public void push(int value) {
        // 判断栈是否已经满了
        if (isFull()) {
            System.out.println("栈已经满了~~");
            return;
        }

        // 栈顶++
        top++;
        // 存入数据
        stack[top] = value;
    }

    /**
     * 出栈 将栈顶数据返回
     * @return int
     */
    public int pop() {
        // 判断是否为空
        if (isEmpty()) {
            System.out.println("栈已经空了~~");
            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]);
        }
    }

}

栈实现计算器

思路分析

在这里插入图片描述

  1. 初始化两个栈,一个【数栈】和一个【符号栈】。
  2. 通过一个index值(索引),来遍历表达式。
  3. 如果发现是一个数字,就直接入【数栈】。
  4. 如果发现是一个运算符,就分情况讨论:
    1. 如果当前的【符号栈】为空,那就直接入栈。
    2. 如果当前的【符号栈】有操作符,就进行比较 :
      1. 【当前的操作符】的优先级 <= 【符号栈中操作符】的优先级,就需要从【数栈】中pop出两个数,再从【符号栈】中pop出一个符号,然后进行运算,将得到结果入【数栈】。然后递归第4步,直到将【当前的操作符】push到【符号栈】为止。
      2. 【当前的操作符】的优先级 > 【符号栈中操作符】的优先级,就直接入符号栈。
  5. 当表达式扫描完毕,就顺序的从【数栈】和【符号栈】中pop相应的数和符号,并运算,将结果push到数栈。
  6. 最后【数栈】中只有一个数字就是表达式的结果。

代码实现

在这里插入图片描述

/**
 * @ClassName Calculator
 * @author: shouanzh
 * @Description 使用栈实现简单的计算器
 * @date 2022/4/30 19:57
 */
public class Calculator {
    public static void main(String[] args) {

        // 运算表达式
        String expression1 = "33+2+2*6-2";
        String expression2 = "7*22*2-5+1-5+3-4";
        String expression3 = "4/2*3-4*2-3-99";
        String expression4 = "1*1*1*3*2/3";
        String expression5 = "11*1*1*3*2/3";
        String expression6 = "1000*23";
        String expression7 = "1-1*1+9";

        // 创建两个栈:数栈、符号栈
        ArrayStack numStack = new ArrayStack(10);
        ArrayStack operationStack = new ArrayStack(10);

        test(expression1, numStack, operationStack);
        test(expression2, numStack, operationStack);
        test(expression3, numStack, operationStack);
        test(expression4, numStack, operationStack);
        test(expression5, numStack, operationStack);
        test(expression6, numStack, operationStack);
        test(expression7, numStack, operationStack);
    }

    /**
     * 测试方法,测试表达式的结果,并且打印结果
     * @param expression 表达式
     * @param numStack 数字栈
     * @param operationStack 符号栈
     */
    public static void test(String expression, ArrayStack numStack, ArrayStack operationStack) {
        // 用于扫描
        int index = 0;
        // 将每次扫描得到的char保存到ch
        char ch;

        // 开始while循环的扫描expression
        while (true) {
            // 依次得到expression的每一个字符
            ch = getCharByIndex(expression, index);
            // 判断ch是什么,然后做相应的处理
            if (isOperation(ch)) {
                // 运用管道过滤器风格,处理运算符
                operationSolve1(ch, numStack, operationStack);
            } else {
                // 数直接入数栈,对值为ASCII值-48
                // 当处理多位数时候,不能立即入栈,可能是多位数,调用过滤器处理多位数
                index = numSolve1(expression, index, numStack);
            }
            // 让index+1,并判断是否扫描到expression最后
            index++;
            if (index >=  expression.length()) {
                break;
            }
        }
        // 最后只剩下两个数和一个运算符
        int res = cal((int) numStack.pop(), (int) numStack.pop(), (char) operationStack.pop());
        System.out.printf("表达式: %s = %d\n", expression, res);
    }

    /**
     * 获取表达式的下标位置为index的字符
     * @param expression 表达式
     * @param index 下标
     * @return
     */
    public static char getCharByIndex(String expression, int index) {
        return expression.charAt(index);
    }

    /**
     * 处理数字入栈的情况,包含处理多位数的情况,并且返回到操作表达式当前的下标
     * @param expression 表达式
     * @param index 下标
     * @param numStack 数字栈
     * @return 新的下标
     */
    public static int numSolve1(String expression, Integer index, ArrayStack numStack) {
        // 在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈
        int end = index + 1;
        for (; end < expression.length(); end++) {
            char ch = getCharByIndex(expression, end);
            // 判断是不是数字
            if (!isOperation(ch)) {
                continue;
            } else {
                break;
            }
        }
        String numStr = expression.substring(index, end);
        // 数据入栈
        numStack.push(Integer.parseInt(numStr));
        // 因为test函数进行了+1,所以这里进行-1,避免给重复添加
        return end - 1;
    }

    /**
     * 符号过滤器1,判断当前是否具有字符
     * @param ch 运算符
     * @param numStack 数字栈
     * @param operationStack 运算符栈
     */
    public static void operationSolve1(char ch, ArrayStack numStack, ArrayStack operationStack) {
        // 判断当前符号栈是否具有操作符
        if (!operationStack.isEmpty()) {
            operationSolve2(ch, numStack, operationStack);
        } else {
            operationStack.push(ch);
        }
    }

    /**
     * 符号过滤器2,处理字符优先级,递归调用过滤器1
     * @param ch 运算符
     * @param numStack 数字栈
     * @param operationStack 运算符栈
     */
    public static void operationSolve2(char ch, ArrayStack numStack, ArrayStack operationStack) {
        // 比较优先级
        if (priority(ch) <= priority((Character) operationStack.peek())) {
            // 调用过滤器3进行计算
            operationSolve3(numStack,operationStack);
            // 递归调用过滤器1,不能递归调用过滤器2,因为可能存在当前运算符栈为空的情况
            operationSolve1(ch, numStack, operationStack);
        } else {
            // 直接将运算符加入到运算符栈中
            operationStack.push(ch);
        }
    }

    /**
     * 符号过滤器3,进行运算
     * @param numStack 数字栈
     * @param operationStack 运算符栈
     */
    public static void operationSolve3(ArrayStack numStack, ArrayStack operationStack) {
        // 定义相关变量
        int num1 = (int) numStack.pop();
        int num2 = (int) numStack.pop();
        char operation = (char) operationStack.pop();
        int res = cal(num1, num2, operation);
        // 把运算结果加到数栈
        numStack.push(res);
    }

    /**
     * 返回运算符的优先级,数字越大,运算符越高
     * @param operation 运算符
     * @return
     */
    public static int priority(char operation) {
        if (operation == '*' || operation == '/') {
            return 1;
        } else if (operation == '+' || operation == '-') {
            return 0;
        } else {
            // 假设目前的表达式只有 + - * /
            return -1;
        }
    }

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

    /**
     * 计算结果
     * @param num1 操作数1,先出栈的数
     * @param num2 操作数2,后出栈的数
     * @param operation 操作符
     * @return 计算结果
     */
    public static int cal(int num1, int num2, char operation) {
        // 用于存放运算的结果
        int res = 0;
        switch (operation) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                // num1是先弹出来的数,为被减数
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                // num1是先弹出来的数,为被除数
                res = num2 / num1;
            default:
                break;
        }
        return res;
    }
}

/**
 * 定义一个ArrayStack表示栈
 */
class ArrayStack {

    // 栈的大小
    private int maxSize;

    // 数组模拟栈,数据就放在该数组中
    private Object[] stack;

    // top表示栈顶,初始化为-1
    private int top = -1;

    // 构造器 初始化栈的大小
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new Object[maxSize];
    }


    /**
     * 判断栈满
     * @return boolean
     */
    public boolean isFull() {
        return top == maxSize - 1;
    }

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

    /**
     * 入栈
     * @param value 数据
     */
    public void push(Object value) {
        // 判断栈是否已经满了
        if (isFull()) {
            System.out.println("栈已经满了~~");
            return;
        }

        // 栈顶++
        top++;
        // 存入数据
        stack[top] = value;
    }

    /**
     * 出栈 将栈顶数据返回
     * @return int
     */
    public Object pop() {
        // 判断是否为空
        if (isEmpty()) {
            System.out.println("栈已经空了~~");
            throw new RuntimeException("栈已经空了~~");
        }

        // 取得栈顶的值
        Object value = stack[top];
        // 栈顶--
        top--;
        // 返回值
        return value;
    }


    /**
     * 遍历栈 遍历时需要从栈顶开始显示数据
     */
    public void list() {
        // 判断是否为空
        if (isEmpty()) {
            System.out.println("栈已经空了~~");
            return;
        }

        for (int i = top; i >= 0; i--) {
            System.out.println(stack[i].toString());
        }
    }


    /**
     * 返回栈顶元素
     * @return Object
     */
    public Object peek() {
        // 判断是否为空
        if (isEmpty()) {
            System.out.println("栈已经空了~~");
            throw new RuntimeException("栈已经空了~~");
        }
        return stack[top];
    }

}

运行结果

表达式: 33+2+2*6-2 = 45
表达式: 7*22*2-5+1-5+3-4 = 298
表达式: 4/2*3-4*2-3-99 = -104
表达式: 1*1*1*3*2/3 = 2
表达式: 11*1*1*3*2/3 = 22
表达式: 1000*23 = 23000
表达式: 1-1*1+9 = 9

Process finished with exit code 0

🥳

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值