栈(数组模拟栈、实现简单计算器)

一、栈的概述

1.1 什么是栈

栈(stack)是一种基于先进后出FILO-First In Last Out)策略的集合类型。栈是一种特殊的线性表,它限制元素的插入和删除只能在同一端进行。允许插入和删除元素的一端,为变化的一端,称为栈顶;另一端为固定的一端,称为栈底

根据栈的定义可以知道:最先放入的元素在栈底,最后放入的元素在栈顶;而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

下图形象地描述了元素出栈和入栈的过程:

图一:元素入栈过程
图二:元素出栈过程

1.2 栈的应用场景

栈作为一种重要的基本数据结构,它的应用是比较广泛的。栈的应用包括如下几个方面:

  1. 子程序的调用:在跳往子程序前, 会先将下个指令的地址存到堆栈中, 直到子程序执行完后再将地址取出, 以
    回到原来的程序中;
  2. 处理递归调用:和子程序的调用类似, 只是除了储存下一个指令的地址外, 也将参数、 区域变量等数据存入堆
    栈中;
  3. 表达式的转换(中缀表达式转后缀表达式)与求值;
  4. 二叉树的遍历;
  5. 图形的深度优先(depth 一 first)搜索法。

二、用数组模拟栈

由于栈是一种有序列表, 当然可以使用数组的结构来储存栈的数据内容。下面我们就用数组模拟栈的出栈, 入栈等操作。

2.1 思路分析

使用数组来模拟栈的思路是比较简单的,按照下面的步骤即可:

  1. 定义一个类,该类的成员变量包括一个数组 stack(用于模拟栈)、两个整型变量 maxSizetop(分别代表栈的大小、栈顶指针);
  2. 栈顶指针 top 初始化为 -1;
  3. 每当有元素要入栈时,top 加 1,然后元素记录在数组中,即 stack[top] = element
  4. 每当有元素要出栈时,先读取数组的元素,即 element = stack[top],然后 top 减 1。

2.2 代码实现

public class No1_StackBasicDemo {
    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(10);
        Scanner input = new Scanner(System.in);

        boolean flag =  true;
        while (flag){
            System.out.println("-------M E N U -------");
            System.out.println("请选择您的操作:");
            System.out.println("push:\t数据入栈");
            System.out.println("pop:\t数据出栈");
            System.out.println("show:\t打印栈数据");
            System.out.println("exit:\t退出程序");
            System.out.println("-------M E N U -------");
            String select = input.nextLine();
            switch (select){
                case "push":	// 入栈
                    System.out.println("请输入要入栈的元素:");
                    int num = Integer.parseInt(input.nextLine());
                    stack.push(num);
                    // 就是为了让程序停一下
                    System.out.print("按下任意键继续:");
                    input.nextLine();
                    break;
                case "pop":		// 出栈
                    try{
                        int pop = stack.pop();
                        System.out.println("出栈的元素为:" + pop);
                    } catch (RuntimeException e){
                        System.out.println(e.getMessage());
                    }
                    // 就是为了让程序停一下
                    System.out.print("按下任意键继续:");
                    input.nextLine();
                    break;
                case "show":	// 打印数据
                    stack.showStack();
                    // 程序停一下
                    System.out.print("按下任意键继续:");
                    input.nextLine();
                    break;
                case "exit":	// 退出程序
                    flag = false;
                    break;
                    default:
                        break;
            }
        }

    }
}

/**
 * @Description 使用数组模拟栈
 */
class ArrayStack{
    private int maxSize;    // 栈的大小
    private int[] array;    // 此数组用于模拟栈
    private int pos;        // 栈的指针

    public ArrayStack(int maxSize){
        this.maxSize = maxSize;
        array = new int[maxSize];
        pos = -1;   // 指针初始指向第 -1 个位置
    }
    // 判断栈是否满了
    public boolean isFull(){
        return pos == maxSize - 1;
    }
    // 判断栈是否为空
    public boolean isEmpty(){
        return pos == -1;
    }
    // 打印栈中数据
    public void showStack(){
        if (isEmpty()){
            System.out.println("栈为空!");
            return;
        }
        System.out.println("============================");
        System.out.println("栈中内容如下:");
        System.out.println("---------");
        for (int i=pos; i>=0; i--){
            System.out.println("|\t" + array[i] + "\t|") ;
            System.out.println("---------");
        }
    }
    // 模拟入栈
    public void push(int num){
        // 1. 首先判断栈是否满了
        if (isFull()){
            System.out.println("栈满了,无法入栈!");
            return;
        }
        // 2. 先把指针加 1,然后入栈
        array[++pos] = num;
        System.out.println(num + " 入栈成功!");
    }
    // 模拟出栈
    public int pop(){
        // 1. 首先判断是否为空
        if (isEmpty()){
            throw new RuntimeException("栈空了,无法出栈!");
        }
        // 2. 先让数据出栈,然后指针减 1
        return array[pos--];
    }
}

三、栈实现简单计算器(中缀)

使用栈来实现一个简单的计算器,该计算器包括最基本的计算功能:加、减、乘、除。

例:

输入:3+2*6-1
输出:14

3.1 思路分析

表达式分为中缀表达式、前缀表达式、后缀表达式。中缀表达式就是表达式本身,如 “3+2*6-1” 就是一个中缀表达式。关于表达式的详细介绍将会在后面的博客中展开说明。

本案例实现的简单计算器就是直接对中缀表达式(也就是原计算表达式)进行分析处理。

如果要实现一个计算器,可以按照以下思路:

  1. 初始化两个栈,一个作为符号栈、一个作为数字栈;

  2. 通过一个索引 index,来从左至右遍历中缀表达式;

  3. 如果遍历到的是一个数字,就直接入数字栈;

  4. 如果遍历到的是一个符号:

    • 如果当前符号栈为空,就直接入符号栈;

    • 如果符号栈有操作符,就进行比较:

      • 若当前的操作符优先级小于或等于栈顶的操作符,就从数字栈中 pop 出两个数,再从符号栈中 pop 出一个符号进行运算。运算得到的结果 push 入数字栈中,然后将当前的操作符入符号栈;
      • 若当前的操作符优先级大于栈顶的操作符,就直接入符号栈;
  5. 中缀表达式遍历完毕之后,就依次从数字栈和符号栈中 pop 出相应的数和符号,对他们进行运算;

  6. 最后在数字栈中将只剩下一个数字,这个数字就是表达式的结果。

3.2 代码实现

Java 提供了栈类 Stack,本例中依然使用的是数组来模拟栈,接下来的博客中将直接使用 Java 提供的 Stack 类。

栈实现中缀表达式的简单计算器的源码如下:

public class No2_Stack_BasicCalculator {

    public static void main(String[] args) {
        String str = "300+20*6-10*1";
        ArrayNumStack numStack = new ArrayNumStack(5);
        ArrayOperStack operStack = new ArrayOperStack(5);
        int num = 0;
        // 具体计算
        int length = str.length();
        for (int index = 0; index < length; index++) {
            char param = str.charAt(index);
            // 判断是不是数字
            if ('0' <= param && param <= '9') {
                // 如果是数字
                num = num * 10 + param - 48;
                // 还需要往后判断一位,看看是数字还是符号,如果是符号就进栈,如果是数字就不急着进栈
                // 但是,需要首先看看后面还有没有数据了
                if (index + 1 < length) {
                    // 如果后面还有数据,就取出来看看是符号还是数字
                    char next = str.charAt(index + 1);
                    if (!(next >= '0' && next <= '9')) {
                        numStack.pushNum(num);
                        num = 0;	// 初始化
                    }
                } else {
                    // 如果后面没数据了,说明到了字符串最后了
                    numStack.pushNum(num);
                }
            } else {
                // 如果不是数字,就是符号
                // 如果符号栈为空,直接进栈
                if (operStack.isEmpty()) {
                    operStack.pushOper(param);
                } else {
                    // 符号优先级比较了,用于判断是否做运算
                    // 先判断将要进栈的符号的优先级和栈顶的符号优先级哪个高
                    int pri_param = getPriority(param);
                    int pri_top = getPriority(operStack.peek());
                    if (pri_param >= pri_top) {
                        // 如果要进栈的符号优先级大于等于栈顶的,则直接进栈
                        operStack.pushOper(param);
                    } else {
                        // 如果要进栈的符号优先级小于等于栈顶的,那么栈顶元素出栈进行运算
                        char oper = operStack.popOper(); 	// 符号栈出一个元素
                        int num_1 = numStack.popNum();		// 数字栈出两个元素
                        int num_2 = numStack.popNum();	
                        int res = calculate(num_1, num_2, oper);	// 运算
                        numStack.pushNum(res);		// 运算结果进栈
                        operStack.pushOper(param);	// 运算符进栈
                    }
                }
            }
        }
        // 经过上面的操作,符号栈剩下的都是优先级相等的符号了,直接出栈做运算就可以啦
        // 如果符号栈为空,说明已经计算完了
        while (!operStack.isEmpty()) {
            char oper = operStack.popOper();
            int num_1 = numStack.popNum();
            int num_2 = numStack.popNum();
            int res = calculate(num_1, num_2, oper);
            numStack.pushNum(res);
        }
        // 这个时候,数字栈的最后一个元素,就是最后的计算结果
        System.out.println(str + " = " + numStack.popNum());
    }
    // 获取运算符优先级,数字越大,优先级越大
    private static int getPriority(char oper) {
        if (oper == '+' || oper == '-') {
            return 0;
        }
        if (oper == '*' || oper == '/') {
            return 1;
        }
        return -1;
    }
    // 给定两个数字以及运算符,计算出结果
    private static int calculate(int num_1, int num_2, char oper) {
        int result = 0;
        switch (oper) {
            case '+':
                result = num_1 + num_2;
                break;
            case '-':
                result = num_2 - num_1;
                break;
            case '*':
                result = num_1 * num_2;
                break;
            case '/':
                result = num_2 / num_1;
            default:
                break;
        }
        return result;
    }

}

/**
 * @Description 使用数组模拟数字栈
 */
class ArrayNumStack {
    private int maxSize;            // 栈的容量
    private int[] numStack;        // 存放数字的栈
    private int top;            // 数字栈的栈顶

    public ArrayNumStack(int maxSize) {
        this.maxSize = maxSize;
        numStack = new int[maxSize];
        top = -1;
    }
    // 数字入栈
    public void pushNum(int num) {
        if (isFull()) {
            System.out.println("数字栈满了!");
            return;
        }
        numStack[++top] = num;
    }
    // 数字出栈
    public int popNum() {
        if (isEmpty()) {
            throw new RuntimeException("数字栈为空!");
        }
        return numStack[top--];
    }
    // 判断数字栈是否满了
    private boolean isFull() {
        return top == maxSize - 1;
    }
    // 判断数字栈是否为空
    private boolean isEmpty() {
        return top == -1;
    }
}

/**
 * @Description 数组模拟运算符栈
 */
class ArrayOperStack {
    private int maxSize;    // 运算符栈的大小
    private char[] operStack;   // 模拟运算符栈
    private int top;        // 栈顶指针

    public ArrayOperStack(int maxSize) {
        this.maxSize = maxSize;
        operStack = new char[maxSize];
        top = -1;
    }
    // 看一下运算符栈栈顶元素是什么,用于比较运算符优先级
    public char peek() {
        return operStack[top];
    }
    // 运算符入栈
    public void pushOper(char val) {
        if (isFull()) {
            System.out.println("运算符栈满了!");
            return;
        }
        operStack[++top] = val;
    }
    // 运算符出栈
    public char popOper() {
        if (isEmpty()) {
            throw new RuntimeException("运算符栈为空!");
        }
        return operStack[top--];
    }
     // 判断运算符栈是否满了
    private boolean isFull() {
        return top == maxSize - 1;
    }
    // 判断运算符栈是否为空
    public boolean isEmpty() {
        return top == -1;
    }
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值