栈 Stack

  • 先入后出
  • 栈顶Top (变化的一端),栈底Bottom(固定的一端)
  • pop push isEmpty
  • 应用场景:
    子程序调用,处理递归调用,表达式的转换,二叉树的遍历,图的深度优先搜索
1. 数组模拟栈
class myArrayStack{
    private int maxSize;
    private int[] stack;
    private int top=-1; //栈顶,初始值为-1

    //构造器
    public myArrayStack(int maxSize){
        this.maxSize = maxSize;
        stack = new int[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("栈空,无法出栈~");
        }
        return stack[top--];
    }
    
    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]);
        }
    }
}
2. 单向链表模拟栈(头插法)
class LinkedListStack{
    //初始化头节点
    private  Node head = new Node(-1);

    public boolean isEmpty(){
        return head.next==null;
    }

    //头插法  模拟 入栈
    public void push(Node node){
        Node top = head.next; //第一个节点
        node.next = top;
        head.next = node;
    }
    //出栈
    public Node pop(){
        if (isEmpty()){
            throw new RuntimeException("栈空,无法出栈~");
        }
        Node top = head.next;
        head.next = top.next;
        return top;
    }
    //遍历
    public void list(){
        if (isEmpty()){
            System.out.println("栈空,无法遍历~");
            return; //停止函数
        }
        Node top = head.next;
        int i=0;
        while (top!=null){
            System.out.printf("stack[%d]=%d\n",i,top.value);//stack[i]为逆序
            i++;
            top = top.next;
        }
    }

}

栈实现计算器

单个数字之间的 加减乘除: "3+2*6-2"

  1. 索引index 遍历表达式

  2. 创建数栈 numStack,符号栈 operStack(分别存数字num 与操作符oper )

  3. 符号栈:

    • 运算符优先级 oper <= operStack[top] :

      numStack.pop 两个数 + operStack.pop一个符号进行运算 【b x a】

      numStack.push运算结果,operStack.push 操作符

    • 符号栈为空,操作符优先级 oper > operStack[top],直接入栈

  4. 表达式扫描完毕后,pop 数栈与符号栈 进行运算

  5. 直到 数栈只有一个数字时,得到表达式结果

前,中,后缀表达式

(扫描到 运算符pop出两个数字进行运算,只需要一个栈【用来存数字】)

  1. 前缀表达式:
    • 从右至左扫描表达式, 栈顶 * 次顶 (*为运算符)
    • 【- X + 3 4 5 6】 ---> (3+4) X 5 - 6
  2. 中缀表达式:
    • 常见的运算表达式
  3. 后缀表达式:(逆波兰表达式)
    • 从左至右扫描, 次顶 * 栈顶
    • 【3 4 + 5 X 6 -】---> (3+4) X 5 - 6

逆波兰计算器

  • 题目:
    (3+4)x5-6 => [ 3 4 + 5 x 6 - ] 先中缀转后缀
    输入后缀表达式(逆波兰表达式),使用栈 计算其结果 29
    支持 括号和多位整数
    表达式输入用 空格隔开
  • 思路:
    1. 先把表达式的值 放到数组里面
    2. 遍历数组,压入栈
      • 数字:直接入栈
      • 运算符:pop 次顶元素 * 栈顶元素 (*代表运算符)
逆波兰计算器 代码实现
public class PolandNotation {
    public static void main(String[] args) {
        String suffixExpression = "3 4 + 5 x 6 -";
		List list = getListString(suffixExpression);
        int res = calculate(list);
        System.out.printf("逆波兰表达式:%s = %d",suffixExpression,res);
    }

    //1.创建数组 存表达式
    public static List getListString(String expression){
        List<String> list = new ArrayList<>();

        String[] listString = expression.split(" ");
        for (String s : listString) {
            list.add(s);
        }
        return list;
    }

    //2.遍历数组 入栈 进行计算
    public static int calculate(List<String> list){
        Stack<String> stack = new Stack<>();//创建栈

        for (String s : list) {
            if (s.matches("\\d+")){ //匹配多个数字
                stack.push(s);
            }else{                        //遇到运算符时,进行运算 次顶*栈顶
                int num1 = Integer.parseInt(stack.pop()); //栈顶
                int num2 = Integer.parseInt(stack.pop()); //次顶 (字符串转int)
                int res = 0;
                if (s.equals("+")){
                    res = num2 + num1;
                }else if (s.equals("-")){
                    res = num2 - num1;
                }else if (s.equals("x")){
                    res = num2 * num1;
                }else if (s.equals("/")){
                    res = num2 / num1;
                }else {
                    throw new RuntimeException("运算符有误");
                }
                stack.push(""+res);  //结果入栈 (int转字符串)
            }
        }
        return Integer.parseInt(stack.pop()); //弹出最后一个数(即结果)
    }
}

中缀转后缀表达式

  1. 初始化两个栈:运算符栈s1 和 存储中间结构的栈s2
  2. 从左到右 扫描中缀表达式
    • 遇到数字:压入s2
    • 遇到运算符:
      1. s1为 或 栈顶为左括号(优先级高于栈顶运算符:压入s1;
      2. 否则将 s1的栈顶pop 放到s2中,继续比较。
    • 遇到括号:
      1. 左括号( ,直接压入s1;
      2. 右括号) ,不断pop s1放到s2 直到遇到左括号,将这对括号丢弃。
  3. 扫描完毕后,将s1剩余的元素 pop到s2中
  4. 依次弹出s2的元素,结果的逆序即为 后缀表达式。

    ( 栈s2没有pop操纵,故可以将s2用数组代替,就不用再逆序)

中缀转后缀表达式 代码实现
//中缀 转后缀
public static List  toSuffixExpression(List<String> ls){
    //1.初始化两个栈
    Stack<String> s1 = new Stack<>(); //运算符栈
    List<String> s2 = new ArrayList<>(); //存放中间结果 list替代stack

    //2.遍历中缀表达式
    for (String item : ls) {
        if (item.matches("\\d+")){  //a匹配到数字
            s2.add(item);
        }else if (item.equals("(")){      //b匹配到左括号
            s1.push(item);
        }else if (item.equals(")")){      //c匹配到右括号
            while (! s1.peek().equals("(")){ //s1栈顶不是 ( 时 !!!!!
                s2.add(s1.pop());  //在匹配到左括号前 不断把pop(s1) 放到s2中
                //System.out.println("s2: "+s2.toString());
            }
            s1.pop(); //并消除这对括号
        }else {                          //d匹配到运算符
            
            //d2 否则,s1的元素pop到s2中,并继续匹配
            while(s1.size() !=0 && priority(item) <= priority(s1.peek())){
                s2.add(s1.pop());
            }
            //d1 匹配到 栈顶为空 或 运算符优先级 大于栈顶运算符
            s1.push(item);  //直接入栈
        }
    }
    //3. 扫描完后,将s1剩余元素pop到s2中
    while (s1.size() != 0){
        s2.add(s1.pop());
    }
    //4. s2剩下的元素弹出  结果的逆序就是后缀表达式 --> s2转用数组,直接遍历就是结果
    return s2;
}

//字符串转数组
public static List getInfixList(String str){
    //先把字符串转为数组
    List<String> list = new ArrayList<>();
    int i = 0;    //记录位置
    String num;  //拼接多位数
    char ch;    //代表每一位字符
    do {
        ch = str.charAt(i); //获取第i个字符
        if (ch <48 || ch>57){   //为运算符的情况
            list.add(""+ch);            //字符转字符串
            i++;
        }else {                 //为数字的情况
            num="";
            while (i < str.length() && str.charAt(i)>=48 &&str.charAt(i)<=57){ //ascll码 48-57代表数字0-10
                num+=str.charAt(i);
                i++;            //字符后移
            }
            list.add(num);
        }
    }while (i < str.length());
    return list;
}

//获取运算符优先级
public static int priority(String oper){
    if (oper.equals("+") || oper.equals("-")){
        return 1;
    }else if (oper.equals("x") || oper.equals("/")){
        return 2;
    }else
        return 0;
}
posted @ 2022-02-07 22:30  simp1e1  阅读(75)  评论(0)    收藏  举报