数据结构与算法——栈

发布于:2022-10-12 ⋅ 阅读:(478) ⋅ 点赞:(0)

目录

1.栈的模拟

2.使用栈完成表达式的计算

3.前缀、中缀、后缀


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

1.栈的模拟

  使用数组模拟栈:(也可以使用单链表来表示 )

  注:在数据入栈和出栈时,必须先判断栈满或栈空!

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

1)Top表示栈顶,初始化为-1

class ArrayStack{
    private int maxSize;   //栈的大小
    private int top = -1;   //栈顶,无数据时初始化为-1
    private int[] stack;  //数组模拟栈,数据放在该数组中

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }
}

2)数据入栈时,Top++,stack[Top]

//入栈操作
public void push(int value){
    if(isFull()){
        System.out.println("栈满!");
        return;
    }
    top++;
    stack[top] = value;
}

3)数据出栈时,int value = stack[Top] ; Top-- ; return value

//出栈操作
public int pop(){
    if(isEmpty()){
        throw new RuntimeException("栈空!");  //根据方法结构,此处必须要有返回值,但栈空时,没有数据要返回,所以此处吃用抛出异常来提示栈空!
    }
    int value = stack[top];
    top--;
    return value;
}

4)遍历栈

//遍历栈
public void List(){
    if(isEmpty()){
        System.out.println("栈空!");
        return;
    }
    for (int i = top; i >= 0; i--) {
        System.out.println("stack[" + i + "] = " + stack[i]);
    }
}

2.使用栈完成表达式的计算

验证:3+2*6-2 = ?

1) 创建一个类,表示栈结构(在栈的模拟中创建的类中添加几个可以用到的方法)

        a.返回运算符的优先级

public int priority(int oper){
     if(oper == '*'  || oper == '/'){
         return 1;    //设置优先级为1
     }else if(oper == '+' || oper == '-'){
         return 0;
     }else {
         return -1;   //目前只含有 + - * /
     }
 }

        b.判断是否为运算符(or 数字)

 public boolean isOper(char val){
     return val == '+' || val == '-' || val == '*' || val == '*';
 }

        c.数值之间的计算

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;
    }

        d.查看栈顶数据(主要使用在运算符栈中,需要判断比较扫描道德运算符与栈顶的运算符的优先级),不是出栈

public int showTop(){
        return stack[top];
    }

2)创建两个栈,并定义需要的变量

//根据思路分析,完成表达式的运算
String expression = "3+3*6-2";
//创建两个栈,一个数栈一个符号栈
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中

3)使用 index 指针扫描 expression,并进行逻辑判断

思路:

  a.通过一个index值(索引),遍历表达式

  b.如果是数字,就直接入数栈

  c.如果是符号,就分情况

        i.如果发现当前符号栈为空,就直接入栈(防止栈中符号重复)

        ii.如果符号栈有操作符,则进行比较,若当前操作符的优先级小于或等于栈中的操作符,就需要从数栈中pop出两个数,再从符号栈中pop出一个符号,进行运算后,再将得到的结果入数栈,再将当前的操作符入符号栈;若当前操作符的优先级大于栈中的操作符,就直接入符号栈

while(true){
    //一次得到 expression 中的每一个字符
    ch = expression.substring(index,index+1).charAt(0);   //1.每次从字符串中取一个ch组成一个新的字符数组,后读取(因为只取出一个元素,所以新数组中只有一个
    //判断是数字还是运算符
    if(operStack.isOper(ch)){  //2.1如果是运算符,则需要判断栈中是否为空,若不为空则需要判断与已有运算符的优先级
        if(!operStack.isEmpty()) {  //3.1判断运算符栈是否为空
            //4.判断与已有运算符的优先级
            if (operStack.priority(ch) <= operStack.priority(operStack.showTop())) {  //4.1若后入运算符优先级小于或等于前入运算
                num1 = numStack.pop();
                num2 = numStack.pop();
                oper = operStack.pop();
                res = operStack.cal(num1, num2 , oper); //5.再将运算后的值,push进数栈
                numStack.push(res);  // 6.再将目前扫描的运算如入操作符栈
                operStack.push(ch);
            } else {  //4.2若当前操作符的优先级大于栈中的操作符,直接入符号栈
                operStack.push(ch);
            }
        }else {  //3.2若运算符栈为空
            operStack.push(ch);
        }
    }else {  //2.2 若不是运算符(则是数字)
        numStack.push(ch - 48);  //直接存存的是ASCII码,数字的ASCII码是从49开始,所以想直接存数字,则需要减去48
    }
    //让 index + 1 ,并判断是否扫描到  expression 的最后
    index++;
    if(index >= expression.length()){
        break;
    }
}

4)扫描完毕过后,就顺序的从数栈和符号栈中pop出响应的符号并运算。最后,数栈中只有一个数字(既表达式结果)

while(true){
    //如果符号栈为空则计算结束,到最后的结果(数栈中只有一个数字【结果】)
    if(operStack.isEmpty()){
        break;
    }
    num1 = numStack.pop();
    num2 = numStack.pop();
    oper = operStack.pop();
    res = operStack.cal(num1, num2 , oper);
    numStack.push(res);
}
System.out.println("表达式: " + expression  + " = "  + numStack.pop());

此代码中没有加入“()”、多位数等细节问题,有待优化

 5)多位数计算

只需在代码步骤2.2入数栈时,需要像 expression 的表达式的 index 后再看一位,如果是数,则继续向后扫描,直至不是数字为止。因此需要定义一个变量(字符串),用于拼接(keepNum)

keepNum += ch;
//判断下一位字符是否为数字,如果是数字则继续扫描
//只是看一位,并不是idex后移
//若 ch 已经是 expression 的最后一位,则直接入栈
if(index == expression.length()-1){
    numStack.push(Integer.parseInt(keepNum));
}else if(operStack.isOper(expression.substring(index+1,index+2).charAt(0))){  //表达式在idex指向和idex后一位截取,后取新数组的第一个元素,看是否是运算符
    //如果后一位是运算符,则入栈(keepNum = '1' 或者 "123",是字符串,需要转为int类型入数栈)
    numStack.push(Integer.parseInt(keepNum));
    //重要!!!在keepNum存入栈之后,要进行清空操作!否则下一次拼接会再次基础上继续拼接!
    keepNum = "";
}

3.前缀、中缀、后缀

 详见:

https://blog.csdn.net/qq_50233116/article/details/127272370?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127272370%22%2C%22source%22%3A%22qq_50233116%22%7D


网站公告

今日签到

点亮在社区的每一天
去签到