数据结构和算法(第五章栈)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

数据结构和算法-第五章栈


提示:以下是本篇文章正文内容,下面案例可供参考

一、栈的介绍

1. 栈的介绍

在这里插入图片描述

在这里插入图片描述

2. 栈的应用场景

在这里插入图片描述

二、数组模拟栈

1. 数组模拟栈分析

在这里插入图片描述

2.数组模拟栈代码

public class ArrayStackDemo {
    public static void main(String[] args) {
        //测试ArrayStack
        //先创建一个对象
        ArrayStack arrayStack=new ArrayStack(4);
        //扫描器
        Scanner sc=new Scanner(System.in);
        //控制是否退出菜单
        boolean loop=true;
        String key="";
        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":
                    arrayStack.list();
                    break;
                case"push":
                    System.out.println("请输入一个数");
                    arrayStack.push(sc.nextInt());
                    break;
                case"pop":
                    try{
                        System.out.printf("出栈的数据为%d\n",arrayStack.pop());
                    }catch(Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case"exit":
                    //关闭输入流
                    sc.close();
                    //退出循环
                    loop=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出~");
    }
}
//定义一个ArrayStack表示栈
class ArrayStack{
    private int maxSize;//栈的大小
    private int[] stack;//数组,数组模拟栈,数据就在该数组
    private int top=-1;//top表示栈顶,初始化为-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;
    }
    //入栈——push
    public void push(int value){
        //先判断栈是否满
        if(isFull()){
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top]=value;
    }
    //出栈——pop(将栈顶的数据返回)
    public int pop(){
        //先判断栈是否为空
        if(isEmpty()){
            //抛出异常
            throw new RuntimeException("栈空,没有数据~");
        }
        //因为要把栈顶取出,同时要把栈顶-1,所以需要一个变量来保存取出的值
        int value=stack[top];
        top--;
        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]);
        }
    }
}
```c

3.使用单链表模拟栈代码及分析

  • 使用单链表来模拟栈:注意我们在入栈的时候采用的是链表的头插法
  • 即就是入栈时,将该节点插入到(头节点)和(头节点的下一个节点)之间,遍历的时候从头节点开始遍历,仿照栈先进后出的特点
public class Main {

    public static void main(String[] args) {
        //测试SingleLinkedListStack
        //先创建一个对象
        SingleLinkedListStack arrayStack=new SingleLinkedListStack();
        //扫描器
        Scanner sc=new Scanner(System.in);
        //控制是否退出菜单
        boolean loop=true;
        String key="";
        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":
                    arrayStack.list();
                    break;
                case"push":
                    System.out.println("请输入一个数");
                    Node newNode=new Node(sc.nextInt());
                    arrayStack.push(newNode);
                    break;
                case"pop":
                    arrayStack.pop();
                    break;
                case"exit":
                    //关闭输入流
                    sc.close();
                    //退出循环
                    loop=false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出~");
    }
}
//构建链表的节点有了,接下来就是构建链表了——SingleLinkedListStack类,注意我们在入栈的时候采用的是链表的头插法
class SingleLinkedListStack{
    //定义一个头指针,代表栈顶
    private Node top=new Node(-1);
    //判断栈是否为空
    public boolean isEmpty(){
        return top.getNext()==null;
    }
    //入栈——push--采用的是头插法
    public void push(Node node){
        //插入第一个节点
        if(top.getNext()==null){
            top.setNext(node);
            return;
        }
        //定义一个变量使其指向top的下一个节点,将每个节点插入到头节点和其后一位节点之间
        Node temp=top.getNext();
        top.setNext(node);
        node.setNext(temp);
    }
    //出栈
    public void pop(){
        if(top.getNext()==null){
            System.out.println("栈空,没有数据~");
        }
        System.out.println("节点为:"+top.getNext().getValue());
        //将top后移
        top=top.getNext();
    }
    //遍历栈
    public void list(){
        if(isEmpty()){
            System.out.println("栈为空~");
            return;
        }
        //因为头节点不能动,所以我们需要一个辅助变量来遍历
        Node temp=top.getNext();
        while(true){
            //判断链表是否到最后
            if(temp==null){
                return;
            }
            //输出节点的信息,并将辅助变量后移
            System.out.println("节点为:"+temp);
            temp=temp.getNext();
        }
    }
}
//定义链表节点
class Node{
    private int value;//链表值
    private Node next;//指向下一个节点
    //构造器
    public Node(int value){
        this.value=value;
    }
    //set和get方法
    public int getValue(){
        return value;
    }
    public void setValue(int value){
        this.value=value;
    }
    public Node getNext(){
        return next;
    }
    public void setNext(Node next){
        this.next=next;
    }
    //重写toString方法
    @Override
    public String toString() {
        return "HeroNode值为:"+value;
    }
}
```c

4. 数组模拟栈小练

https://leetcode-cn.com/problems/valid-parentheses/submissions/

三、栈实现综合计算器(中缀表达式)

在这里插入图片描述

1. 栈实现综合计算器分析

栈实现综合计算器: 数栈(numStack)存放数 符号栈(operStack)存放运算符
(1)通过一个index值(索引),来遍历我们的表达式
(2)如果发现这是一个数字,就直接加入数栈numStack中
(3)如果发现是一个符号
1.1,如果当前符号栈operStack为空,就直接入栈
2.2,如果符号栈operStack有操作符就进行比较
* * 1,如果当前操作符的优先级大于栈中操作符operStack,就直接加入到符号栈operStack
* * 2,如果当前操作符的优先级小于或者等于栈中的操作符
就需要从数栈中numStack中pop出两个数,再从符号栈operStack中pop出一个符号进行运算,将得到的结果入数栈,然后将当前的操作符加入到符号栈operStack
(4)当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并计算
(5)最后在数栈numStack中只有一个数字,就是表达式的结果

2.栈实现综合计算器代码

public class Calculator {
    public static void main(String[] args) {
        String expression = "70*8-60+20"; // 18//如何处理多位数的问题?
        //创建两个栈,数栈和符号栈
        ArrayStack2 numStack=new ArrayStack2(10);
        ArrayStack2 operStack=new ArrayStack2(10);
        int index=0;//用于扫描的索引
        int num1=0; int num2=0;//弹栈出的数据num1和num2
        int oper=0;//用于接收操作符的oper
        int res=0;//运算得到的结果
        char ch=' ';//将每次扫描得到的char保存到ch
        String keepNum="";//用于拼接多位数
        //开始while循环的扫描expression
        while(true){
            //依次得到exception的每一个字符
            ch=expression.substring(index,index+1).charAt(0);//把字符串转为字符
            //判断ch时字符还是数字
            //是一个字符
            if(operStack.isOper(ch)){
                if(!operStack.isEmpty()){
                    /**
                     * 如果当前符号栈不为空
                     * 1,如果当前操作符的优先级大于栈中操作符operStack,就直接加入到符号栈operStack
                     * 2,如果当前操作符的优先级小于或者等于栈中的操作符
                     *   就需要从数栈中numStack中pop出两个数,再从符号栈operStack中pop出一个符号进行运算,将得到的结果入数栈
                     *   然后将当前的操作符加入到符号栈operStack
                     */
                    if(operStack.priority(ch)<=operStack.priority(operStack.peek())){
                        //从数栈numStack中取出两个数
                        num1=numStack.pop();
                        num2=numStack.pop();
                        //再从符号栈中取出一个符号
                        oper=operStack.pop();
                        //进行运算
                        res=numStack.cal(num1,num2,oper);
                        //把运算得到的结果加入到数栈中
                        numStack.push(res);
                        //然后将当前的操作符加入到符号栈中
                        operStack.push(ch);

                    }else{
                        //如果当前的操作符的优先级大于栈中操作符operStack,就直接加入到符号栈operStack
                        operStack.push(ch);

                    }
                }else{
                    //如果当前符号栈为空,就直接入栈
                    operStack.push(ch);
                }

            }else{
                //如果是数,就直接入数栈
                //扫描到是字符'1',和数字1相差48
                //numStack.push(ch-48);
                /**
                 * 处理多位数
                 * 1,在处理多位数时,不能发现是一个数就立即入栈,因为它可能是多位数
                 * 2,在处理数时,需要向expression的表达式的index后再看一位,如果是数就进行扫描,如果是符号才入栈
                 * 3,因此我们需要定义一个变量字符串,用于拼接
                 */
                //先拼接ch
                keepNum+=ch;
                //如果ch已经是expression的最后一位,就直接入栈
                if(index==expression.length()-1){
                    numStack.push(Integer.parseInt(keepNum));
                }else {
                    //判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,就入数栈numStack
                    //注意是看后一位,不是将index++
                    if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                        //如果后一位是运算符,就直接入数栈,因为此时的keepNum是一个字符串要将它转为一个int
                        numStack.push(Integer.parseInt(keepNum));
                        //注意这个字符串已用完,后面还要使用keepNum,所以要将keepNum清空
                        keepNum = "";
                    }
                }
            }
            //让index加1,并判断是否扫描到exception最后
            index++;
            if(index>=expression.length()){
                break;
            }
        }
        //(4)当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并计算
        //(5)最后在数栈numStack中只有一个数字,就是表达式的结果
        while(true){
            //如果符号栈为空,则计算到最后的结果,数栈中只有一个数字(结果)
            if(operStack.isEmpty()){
                break;
            }
            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());
    }
}
//定义一个 ArrayStack2 表示栈
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];//对数组进行初始化
    }
    //增加一个方法,可以返回当前栈顶的值,但不是pop
    public int peek(){
        return stack[top];
    }
    //栈满
    public boolean isFull(){ return top==maxSize-1; }
    //栈空
    public boolean isEmpty(){ return top==-1; }
    //入栈--push
    public void push(int value){
        //判断栈是否已满
        if(isFull()){
            System.out.println("栈已满,不能添加到栈中~");
            return;
        }
        top++;
        stack[top]=value;
    }
    //出栈--pop
    public int pop(){
        //判断栈是否为空
        if(isEmpty()){
            throw new RuntimeException("栈空~");
        }
        int value=stack[top];
        top--;
        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;//res用于存放计算的结果
        switch(oper){
            case'+':
                res=num2+num1;
                break;
            case'-':
                res=num2-num1;
                break;
            case'*':
                res=num2*num1;
                break;
            case'/':
                res=num2/num1;
                break;
            default:
                break;
        }
        return res;
    }
}


```c

3. 栈实现综合计算器小练

https://leetcode-cn.com/problems/calculator-lcci/submissions/

四、前缀,中缀,后缀表达式规则

1. 前缀表达式的计算机求值

在这里插入图片描述

2. 中缀表达式

中缀表达式就是常见的运算表达式(一般要转为后缀表达式)

3. 后缀表达式

1,后缀表达式
在这里插入图片描述
2,后缀表达式计算机求值
在这里插入图片描述

五、逆波兰计算器

1. 逆波兰计算器分析(即为后缀表达式的计算机求值)

思路:
1,先将3 4 + 5 × 6 -放入到ArrayList中
2,将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算
在这里插入图片描述

2.逆波兰计算器代码

//1.写一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
    public static List<String> getListString(String suffixExpression) {
        //将suffixExpression按空格分割
        String[] split = suffixExpression.split(" ");
        //创建集合
        List<String> list = new ArrayList<String>();
        //将分割出的数据和运算符放入到ArrayList中
        for (String ele : split) {
            list.add(ele);
        }
        return list;
    }
/**
2,完成对逆波兰表达式的计算
    (1),遍历list集合,如果是数就入栈
    (2),如果是运算符就从栈中弹出两个数,进行运算,然后将结果入栈
    (3),最后留在栈中的就是运算结果    
*/    
public static int calculate(List<String> list) {
        //创建栈,只需要一个栈即可
        Stack<String> stack = new Stack<String>();
        //遍历list集合     /item一件物品,元素/
        for (String item : list) {
            //如果是数,就入栈(使用正则表达式)
            if (item.matches("\\d+")) {
                //匹配的是多位数,就入栈
                stack.push(item);
            } else {
                //如果是运算符,就从栈中弹出两个数,计算它们的值,然后再将结果入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")) {
                    res = num1 + num2;
                } else if (item.equals("-")) {
                    res = num1 - num2;
                } else if (item.equals("*")) {
                    res = num1 * num2;
                } else if (item.equals("/")) {
                    res = num1 / num2;
                } else {
                    throw new RuntimeException("运算符错误~");
                }
                //然后将运算得到的值入栈
                stack.push("" + res);
            }
        }
        //最后留在stack中的是运算结果
        return Integer.parseInt(stack.pop());
    }
```c

3. 力扣题解

public int evalRPN(String[] tokens) {
    /**
    1,遍历字符串数组,如果是数就加入到栈中
    2,如果是运算符,就弹出栈顶的两个数,用运算符对它们做相应的运算,并将结果入栈
    3,最后留在栈中的就是运算结果
    */
        //创建栈
        Stack<Integer> stack=new Stack<Integer>();
        //遍历字符串数组,如果是数就加入到栈中
        for(String item:tokens){
            switch(item){
                case "+":
                      stack.push(stack.pop()+stack.pop());
                      break;
                case "-":
                      //2,如果是运算符,就弹出栈顶的两个数,用运算符对它们做相应的运算,并将结果入栈
                      int a=stack.pop();
                      stack.push(stack.pop()-a);
                      break;
                case "*":
                      stack.push(stack.pop()*stack.pop());
                      break;
                case "/":
                      //2,如果是运算符,就弹出栈顶的两个数,用运算符对它们做相应的运算,并将结果入栈
                      int b=stack.pop();               
                      stack.push(stack.pop()/b);
                      break;
                default:
                      //如果不是符号,就是数直接入栈
                      stack.push(Integer.parseInt(item));
                      break;
            }
        }
        // 3,最后留在栈中的就是运算结果
        return stack.pop();
    }



```c

4. 逆波兰计算器小练

https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/

六、中缀表达式转后缀表达式

1. 中缀表达式转后缀表达式分析

在这里插入图片描述

2.中缀表达式转后缀表达式代码

//由于对字符串直接扫描不方便
//1,方法:将中缀表达式转成对应的List
    public static List<String> toInfixException(String s) {
        //定义一个list,存放中缀表达式,对应的内容
        List<String> list = new ArrayList<String>();
        //这是一个指针,用于遍历中缀表达式字符串
        int i = 0;
        //对多位数的拼接
        String str;
        //每遍历到一个字符就放到c
        char c;
        do {
            //如果c是一个非数字,就加入到list
            if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
                list.add("" + c);
                i++;//i需要后移
            } else {
                //如果是一个数,需要考虑多位数,将其进行拼接
                str = "";//先将str置为空,'0'->[48]//'9'->[57]
                while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57) {
                    str += c;//拼接
                    i++;
                }
                //将拼接后的多位数加入到list
                list.add(str);
            }
        } while (i < s.length());
        return list;//返回链表
    }
//2. 将得到的中缀表达式对应的 List => 后缀表达式对应的 List
// 即ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]
public static List<String> parseSuffixExpressionList(List<String> ls) {
        //1) 初始化两个栈:运算符栈 s1 和储存中间结果的栈 s2;
        //符号栈s1
        Stack<String> s1 = new Stack<String>();
        //因为s2这个栈,中间没有使用pop操作,并且最后还需要逆序输出,因此比较麻烦,所以我们不用Stack<String>而是用List<String>
        List<String> s2 = new ArrayList<String>();
        //遍历中缀表达式
        for (String item : ls) {
            if (item.matches("\\d+")) {
                //3) 遇到操作数时,将其压 s2;
                s2.add(item);
            } else if (item.equals("(")) {
                s1.push(item);
            } else if (item.equals(")")) {
                //如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这 一对括号丢弃
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();//将(括号移除s1,消除小括号
            } else {
                //4) 遇到运算符时,比较其与 s1 栈顶运算符的优先级:  缺少一个比较优先级的算法
                //当 item 的优先级小于等于 s1 栈顶运算符, 将 s1 栈顶的运算符弹出并加入到 s2 中,再次转到(4.1) 与 s1 中新的栈顶运算符相比较
                while (s1.size()!=0 && getValue(item) <= getValue(s1.peek())) {
                    s2.add(s1.pop());
                }
                //1.如果 s1 为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
                //     *    2.否则,若优先级比栈顶运算符的高,也将运算符压入 s1;
                s1.push(item);
            }
        }
        // 7) 将 s1 中剩余的运算符依次弹出并压入 s2
        while (s1.size() != 0) {
            s2.add(s1.pop());
        }
        //注意因为是存放到 List有序的, 因此按顺序输出就是对应的后缀表达式对应的 List
        return s2;
    }
    //写一个方法getValue,返回对应的优先级数字getValue
    public static int getValue(String operation) {
        int result = 0;
        switch (operation) {
            case "+":
                result = 1;
                break;
            case "-":
                result = 1;
                break;
            case "*":
                result = 2;
                break;
            case "/":
                result = 2;
                break;
            default:
                //System.out.println("不存在该运算符");
                break;
        }
        return result;
    }


```c

3. 中缀表达式转后缀表达式小练

https://leetcode-cn.com/problems/basic-calculator-ii/

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值