栈是一种新进后出的数据结构

 

用数组实现一个栈

public class ArrStack {
    //用数组模仿栈
    private int maxsize;//定义栈的容量
    private int top=-1;//栈顶指针
    private int buttom=-1;//栈底指针
    private int[] stack;//栈数组

    public  ArrStack(int maxsize){
        //栈的构造器
        this.maxsize=maxsize;
        stack = new int[maxsize];
    }

    private boolean isFull(){
        //判断栈是否为满
        return top==maxsize-1;
    }

    private boolean isEmpty(){
        //判断栈是否为空
        return top==buttom;
    }

    public int  pop(){
        //出栈操作 取出栈顶元素,将栈顶指针减一
        if(isEmpty()){
            throw new RuntimeException("栈空,不能取出数据");
        }
        int value=stack[top];
        top--;
        return value;
    }

    public void push(int value){
        //入栈操作 将栈顶指针加一,将要加入的元素存入该位置
        if(isFull()){
            System.out.println("栈满,不能添加数据");
            return;
        }
        top++;
        stack[top]=value;
    }

    public void showall(){
        //遍历
        if(isEmpty()){
            System.out.println("栈是空的");
            return;
        }
        for (int i = 0; i <top+1 ; i++) {
            System.out.printf("stack[%d]=%d\n",i,stack[i]);
        }
    }
}

用链表实现一个栈

public class LikendStack {

    //使用双向链表构建栈

    private Node head=new Node(-1);//栈底指针
    private  Node tail  = head;//栈顶
     private Node temp;//临时变量,用于栈底指针的修改

     private  boolean isEmpty(){
         //判断栈是否为空(链表可以无限添加,不需要判断是否满)
        return head==tail;
    }

    public int pop(){
         //出栈
        if(isEmpty()){
            throw new RuntimeException("栈空,不能取出数据");
        }
        int value= tail.no;
        tail=tail.pre;//将链表尾部变更为原链表尾部的上一个元素
        return value;
    }
    public void push(int value){
         //入栈
        Node node = new Node(value);
        tail.next=node;
        temp=tail;
        tail=node;//将新加入的链表添加到链表尾部,并将尾部更新为节点
        node.pre=temp;
    }

    public void showall(){
         //遍历
        Node node = tail;
        while(true){
            System.out.println(node.no);
            if (node.pre==head){
                break;
            }
            node=node.pre;
        }
    }

    class Node{
        int no;
        public  Node next;
        public Node pre;
        public Node(int no){
            this.no=no;
        }
    }
}

模仿一个简易计算器

(中缀表达式)

public class Calculator {
    public static void main(String[] args) {
/*
用栈实现一个简易计算器的基本思路:
1.通过index(索引)来遍历表达式
2.如果发现是一个数字则入数栈
3.发现是一个符号则入符号栈
       如果当前的符号优先级小于或等于栈中的符号,从数栈中取出两个数,从符号栈中取出一个符号
       进行运算,将结果存入数栈,并将当前符号入符号栈。
       如果当前的符号优先级大于栈中的符号,并将当前符号入符号栈。
4.表达式扫描完毕以后,顺序的从数栈中取出数据,从符号栈中取出相应的数据进行运算,将每一次的结果入数栈
5.最后数栈中只有一个数字,就是我们需要的结果
 */
        String forString = "3+5+8*3+9/3+20";//将要处理的运算表达式
        ArrStack2 numStack = new ArrStack2(10);//存储数字的栈
        ArrStack2 operStack = new ArrStack2(10);//存储符号的栈
        int index=0;//扫描字符串的索引
        int num1=0;
        int num2 = 0;
        int oper = 0;
        int result = 0;
        char ch=' ';//存储每一个扫描到的字符
        String keepnum="";
        while(true){
            ch = forString.substring(index,index+1).charAt(0);
            if(operStack.isOper(ch)){
                //如果是一个符号,有两种处理逻辑
                if(!operStack.isEmpty()){
                   /* 如果当前的符号优先级小于或等于栈中的符号,从数栈中取出两个数,从符号栈中取出一个符号
                    进行运算,将结果存入数栈,并将当前符号入符号栈。*/
                    if(operStack.priority(ch)<=operStack.priority(operStack.peektop())){
                        num1=numStack.pop();
                        num2=numStack.pop();
                        oper=operStack.pop();
                        result=numStack.caltor(num1,num2,oper);
                        numStack.push(result);
                        operStack.push(ch);
                    }else{
                        //  如果当前的符号优先级大于栈中的符号,并将当前符号入符号栈。
                        operStack.push(ch);
                    }
                }else{
                    //如果是空,则直接入栈
                    operStack.push(ch);
                }
            }else {
                //numStack.push(ch-48);//将字符1转化为数字1(ASCII码表)
                //当发现是一个数字时不能马上入栈,可能是一个多位数
                //应该继续扫描后一个,如果是数字,进行拼接,如果不是入栈

                keepnum+=ch;
                if(index==forString.length()-1){
                    numStack.push(Integer.parseInt(keepnum));
                    //避免字符串的索引越界
                }else {
                    if (operStack.isOper(forString.substring(index+1,index+2).charAt(0))){
                        numStack.push(Integer.parseInt(keepnum));
                        keepnum="";
                    }
                }

            }
            index++;
            if(index>=forString.length()){
                break;
            }
        }
        while(true){
//            4.表达式扫描完毕以后,顺序的从数栈中取出数据,从符号栈中取出相应的数据进行运算,将每一次的结果入数栈
//            5.最后数栈中只有一个数字,就是我们需要的结果
            if (operStack.isEmpty()) break;
            num1=numStack.pop();
            num2=numStack.pop();
            oper=operStack.pop();
            result=numStack.caltor(num1,num2,oper);
            numStack.push(result);
        }
        System.out.printf("表达式的结果是%d",numStack.pop());
    }

    static class ArrStack2 {
        //用数组模仿栈
        private int maxsize;//定义栈的容量
        private int top = -1;//栈顶指针
        private int buttom = -1;//栈底指针
        private int[] stack;//栈数组

        public ArrStack2(int maxsize) {
            //栈的构造器
            this.maxsize = maxsize;
            stack = new int[maxsize];
        }

        private boolean isFull() {
            //判断栈是否为满
            return top == maxsize - 1;
        }

        private boolean isEmpty() {
            //判断栈是否为空
            return top == buttom;
        }

        public int pop() {
            //出栈操作
            if (isEmpty()) {
                throw new RuntimeException("栈空,不能取出数据");
            }
            int value = stack[top];
            top--;
            return value;
        }

        public void push(int value) {
            //入栈操作
            if (isFull()) {
                System.out.println("栈满,不能添加数据");
                return;
            }
            top++;
            stack[top] = value;
        }

        //返回运算符的优先级,靠程序员自己确定
        //数字越大优先级越高
        public int priority(int oper){
                if(oper=='+'||oper=='-'){
                    return 1;
                }else if(oper=='*'||oper=='/'){
                    return 2;
                }else{
                   return -1;
                }
        }

        //判断是否是一个运算符
        public boolean isOper(int value){
            return value=='+'||value=='-'||value=='*'||value=='/';
        }

        //计算方法
        public  int  caltor(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;
        }
        //查看栈顶数据,但是并不取出
        public int peektop(){
            return stack[top];
        }
    }

}

前缀、中缀、后缀表达式

前缀表达式:又被称为波兰表达式,特点是运算符都在数字之前

以 (3+4)*5-6为例中的前缀表达式为: - * + 3 4 5 6

中缀表达式:就是我们最常见的表达式,虽然符合我们个人的习惯,但是并不利于计算机 处理

后缀表达式:又称逆波兰表达式,特点是运算符都在数字之后

中缀表达式转后缀表达式

1.初始化两个栈,运算符栈s1和存储中间结果的栈s2

2.从左至右扫描中缀表达式

3.遇到操作数时,将其压入s2

4.遇到运算符时,比较其余s1栈顶元素的优先级:

  a.如果s1为空或者栈顶运算符为左括号“(”,直接即将运算符入栈;

  b.否则,若优先级比栈顶元素高也直接将该运算符入栈;

  c.否则,将s1栈顶元素压入s2中,再次转到4的开始于新的s1栈顶元素比较

5.遇到括号:

   a.若是左括号“(”,直接入s1;

  b.若是有括号“)”,则依次弹出s1栈顶运算符,并将其压入s2,知道遇到左括号为 止,并将左括号弹出s1(左括号与有括号均不入栈)。

6.重复2-5,直到表达式最右边;

7.将s1剩余的运算符依次压入s2

8.依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

后缀表达式在计算机中的处理

    从左至右扫描表达式,遇到数字时将数字压入栈,遇到运算符时弹出栈顶的两个元素作相应运算,并将结果入栈,重复上述步骤,直到表达式最右端。

 
posted @ 2020-12-07 20:15  素色学习  阅读(111)  评论(0)    收藏  举报