数据结构与算法之栈

前言

在前面几篇我们有讲到数组,链表等数据结构,这些都是存储数据的最基本的结构。而本篇文章讲解的则是利用算法和数据结构来实现一些小工具,来解决现实中某种需求。它就是栈。

什么是栈?

栈(stack) 是一种数据结构,是一个先入后出(Filo-First In Last Out) 的有序列表,只能在一端进行插入和删除操作的特殊线性表。允许插入的一端和删除的一端,为变化的一端,称为 栈顶(top),另一端为固定的一端,称为 栈底(bottom)
它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。

出栈(pop)和入栈(push)示意图

在这里插入图片描述
在这里插入图片描述

实现一个简单的栈

由于栈是一个有序列表,我们可以使用数组来实现一个栈。

代码示例:

public class ArrayStack {

    private int[] dataArray; // 存储栈数据的数组
    private int maxSize; // 栈的最大长度
    private int top; // 指向栈顶部的指针

    public ArrayStack(int maxSize){
        this.maxSize = maxSize;
        this.dataArray = new int[maxSize];
        this.top = -1;
    }

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

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

    /**
     * 入栈
     *
     * @param value
     */
    public void push(int value) {
        if (!this.isFull()) {
            dataArray[++top] = value;
        }
    }

    /**
     * 出栈
     *
     * @return
     */
    public int pop() {
        if (this.isEmpty()) {
            throw new RuntimeException("stack is empty");
        }

        return dataArray[top--];
    }

    /**
     * 获取栈顶部数据
     *
     * @return
     */
    public int peek(){
        return dataArray[top];
    }
}

测试:

public class ArrayStackTest {
    public static void main(String[] args) {
        ArrayStack arrayStack = new ArrayStack(5);
        arrayStack.push(1);
        arrayStack.push(2);
        arrayStack.push(3);
        arrayStack.push(4);
        arrayStack.push(5);

        System.out.println("stack peek:" + arrayStack.peek());

        while (!arrayStack.isEmpty()) {
            System.out.printf("[%d]", arrayStack.pop());
        }
    }
}

输出结果:

stack peek:5
[5][4][3][2][1]

上述代码内部定义了一个数组(dataArray),用来存储栈的数据,maxSize表示栈的最大容量, top是指向栈顶部的指针,初始化时为-1
构造方法传入一个参数:maxSize,用来指定栈的最大容量来创建一个新的栈,push()方法是向栈中放入元素,每放一个top+1,使它指向原顶端数据项上面的一个位置,并在这个位置上存储一个数据。pop()方法返回top变量指向的元素,然后将top变量减一,便实现了数据出栈。

利用栈实现综合计算器

实现思路:

  1. 通过一个 index 值(索引),来遍历我们的计算表达式
  2. 当遍历的当前值是数字的话, 直接放入数据栈中
  3. 当遍历的当前值是字符的话, 就分如下情况
    3.1 如果发现当前的符号栈为空,就直接入栈
    3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中取出两个数,在从符号栈中取出一个符号,进行运算,将得到的结果,入数据栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
  4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并进行计算
  5. 最后在数栈只有一个数字,就是表达式的结果

代码实现:

public class Calculator {

    public static void main(String[] args) {
        ArrayStack numberStack = new ArrayStack(10); // 数据栈
        ArrayStack operatorStack = new ArrayStack(10); // 运算符栈
        String expression = "6*3*9-8+5-7"; // 运算表达式
        // 定义相关临时辅助变量
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';
        String keepNum = "";
        while (true) {
            // 遍历依次得到表达式中的每一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            // 判断ch是什么
            if (Calculator.isOperator(ch)) {// 如果是运算符
                // 判断当前运算符栈是否为空
                if (!operatorStack.isEmpty()) {
                    // 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中取出两个数,在从符号栈中取出一个符号,进行运算,
                    // 将得到的结果,入数据栈,然后将当前的操作符入符号栈
                    if (Calculator.getPriority(ch) <= Calculator.getPriority(operatorStack.peek())) {
                        num1 = numberStack.pop();
                        num2 = numberStack.pop();
                        oper = operatorStack.pop();
                        // 进行计算
                        res = Calculator.cal(num1, num2, oper);
                        // 将计算得到的结果放入数据栈
                        numberStack.push(res);
                        // 将当前的运算符放入运算符栈
                        operatorStack.push(ch);
                    } else {
                        // 如果当前的运算符的优先级大于栈中的运算符,就直接放入运算符栈
                        operatorStack.push(ch);
                    }
                } else {
                    // 直接入运算符栈
                    operatorStack.push(ch); // 1 + 3
                }
            } else { // 如果是数字直接入数据栈
                // 处理多位数
                keepNum += ch;

                // 判断是否是表达式最后一个字符,如果是,直接入栈
                if (index == expression.length() - 1) {
                    numberStack.push(Integer.parseInt(keepNum));
                } else {
                    // 判断下一个字符是不是数字,如果是数字,就继续遍历,如果是运算符,就直接入栈
                    if (Calculator.isOperator(expression.substring(index + 1, index + 2).charAt(0))) {
                        numberStack.push(Integer.parseInt(keepNum));
                        // 清空
                        keepNum = "";

                    }
                }
            }
            // 让index+1,判断表达式是否遍历完了
            index++;
            if (index >= expression.length()) {
                break;
            }
        }

        // 当表达式遍历完了之后,就顺序从数据栈和运算符栈中取出相应的数字和运算符进行计算
        while (true) {
            // 如果运算符栈为空,则代表计算到了最后的结果,数据栈中就只有一个结果数据了
            if (operatorStack.isEmpty()) {
                break;
            }
            num1 = numberStack.pop();
            num2 = numberStack.pop();
            oper = operatorStack.pop();
            // 计算结果
            res = Calculator.cal(num1, num2, oper);
            numberStack.push(res);//ÈëÕ»
        }
        System.out.printf("运算表达式 %s = %d", expression, numberStack.pop());
    }

    /**
     * 返回运算符的优先级
     *
     * @param operator 运算符
     * @return
     */
    public static int getPriority(int operator) {
        if (operator == '*' || operator == '/') {
            return 1;
        } else if (operator == '+' || operator == '-') {
            return 0;
        } else {
            return -1; // 目前只支持 +, - , * , /
        }
    }

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

    /**
     * 计算
     *
     * @param num1 数据一
     * @param num2 数据二
     * @param oper 运算符
     * @return
     */
    public static int cal(int num1, int num2, int oper) {
        int result = 0;
        switch (oper) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num2 - num1;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                result = num2 / num1;
                break;
            default:
                break;
        }
        return result;
    }
}

测试结果:

运算表达式 6*3*9-8+5-7 = 152
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值