数据结构学习五、栈

线性结构-栈

栈介绍

实际需求

输入表达式计算:722-5+1-5+3-3,计算机底层是如何得到的呢?

基本介绍

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

栈的应用场景

  • 子程序的调用
  • 处理递归调用
  • 表达式转换与求值
  • 二叉树的遍历
  • 图的深度优先搜索法

代码实现

数组模拟栈
  • 定义top表示栈顶,初始化为-1
  • 入栈:top++,stack[top] = data
  • 出栈:int value = stack[top],top--,return valuel
package cn.imut;

//表示栈
public class ArrayStack {
    private int maxSize;        //栈的容量
    private int[] stack;        //栈
    private int top = -1;            //栈顶,初始化为-1 (栈顶为最外侧)

    //构造器
    public ArrayStack(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("栈满");
            return;
        }
        top++;
        stack[top] = value;
    }

    //出栈
    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]);
        }
    }
}
测试
package cn.imut;

import java.util.Scanner;
import java.util.Stack;

public class ArrayStackTest {
    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(4);
        String key = "";
        boolean loop = true;

        Scanner sc = new Scanner(System.in);

        while (loop) {
            System.out.println("show:显示栈");
            System.out.println("exit:退出程序");
            System.out.println("push:入栈");
            System.out.println("pop:出栈");
            System.out.println("请输入你的选择");

            key = sc.next();

            switch (key) {
                case "show":
                    stack.list();
                    break;
                case "push":
                    System.out.println("请输入一个数");
                    int value = sc.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":
                    sc.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }
}

栈实现综合计算器

思路

  • 通过一个index值(索引),遍历表达式
  • 发现数字直接入栈
  • 扫描到符号分以下情况
    • 若符号栈为空,直接入栈
    • 若符号栈有操作符,就进行比较
      • 若当前操作符优先级 <= 栈中操作符,就从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,然后将当前操作符入符号栈
      • 若当前操作符优先级 > 栈中操作符,直接入符号栈
  • 当表达式扫描完毕后,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行。
  • 最后在数栈中只有一个数字,就是表达式的结果
验证:3 + 2 * 6 - 2 = 13
  • 3 入数栈 + 入符号栈(第一个符号) 2 入数栈 * 入符号栈(优先级高于+)
  • 6 入数栈
    • 发现优先级小于* 故从数栈出pop两个数 2 6 且从符号栈中pop一个符号 *
    • 将其运算 有 2 * 6 = 12 在将12 入栈
  • 2 入数栈
  • 综上,数栈中有 3 12 2 符号栈有 + -
  • 往出弹两个数 12 2 弹一个符号 - 有 12 - 2 = 10
  • 则 10 + 3 = 13

代码实现

package cn.imut;

//表示栈
public class ArrayStack2 {
    private int maxSize;        //栈的容量
    private int[] stack;        //栈
    private int top = -1;            //栈顶,初始化为-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("栈满");
            return;
        }
        top++;
        stack[top] = value;
    }

    //出栈
    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]);
        }
    }

    //返回运算符的优先级,优先级使用数字表示,数字越大,优先级越高
    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 stack[top];
    }
}
package cn.imut;

import java.awt.event.MouseWheelListener;

public class Calculator {
    public static void main(String[] args) {
        //根据思路分析完成表达式运算
        String expression = "70+2*6-4";
        //创建两个栈,数栈与符号栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);

        //定义需要的相关变量
        int index = 0;  //用于扫描
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';      //每次扫描的char保存到ch
        String keepNum = ""; //用于拼接 多位数
        do {
            //依次得到每一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            //判断ch是什么,然后相应处理
            if (operStack.isOper(ch)) {      //如果是运算符
                //判断当前的符号栈是否为空
                if (!operStack.isEmpty()) {
                    //如果符号栈有操作符,就进行比较,若优先级小,则数栈pop两个数
                    //从符号栈pop一个符号,进行运算,将结果入数栈,当前的操作符入符号栈
                    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 {
                //numStack.push(ch - 48); //? "1+3" '1' => 1
                //分析思路
                //1. 当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数
                //2. 在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈
                //3. 因此我们需要定义一个变量 字符串,用于拼接

                //处理多位数
                keepNum += ch;

                //如果ch已经是expression的最后一位,就直接入栈
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));
                }else{

                    //判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈
                    //注意是看后一位,不是index++
                    if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
                        //如果后一位是运算符,则入栈 keepNum = "1" 或者 "123"
                        numStack.push(Integer.parseInt(keepNum));
                        //重要的!!!!!!, keepNum清空
                        keepNum = "";

                    }
                }
            }
            //让index+1,判断是否扫描到最后
            index++;
        } while (index < expression.length());
        //表达式扫描完毕,顺序的从数栈和符号栈中pop出相应的数与符号,并运行
        while (!operStack.isEmpty()) {
            //若符号栈为空,则计算到最后的结果,数栈中只有一个数字【结果】
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            numStack.push(res);
        }

        System.out.printf("表达式 %s = %d", expression, numStack.pop());
    }
}

猜你喜欢

转载自www.cnblogs.com/yfyyy/p/12459575.html