1.栈----运算受限的线性表


image.png

image.png

image.png

栈的习题:


image.png

image.png

image.png

image.png

单目运算符:eg: ++、-- 、~、^ 等。特点是只有一个操作数,例如a++
image.png

image.png

注意是两个操作数
后缀表达式求值:


image.png

中缀变成后缀:
image.png

image.png

image.png

image.png

image.png

image.png

另一个应用:栈与递归是联系在一起的(理解栈的变化,返回地址不太明白)
image.png

image.png

image.png

解答:
image.png

image.png

image.png

需要注意四点:
一:规定一个方向行走,例如按ESWN、斜着走等
二:将迷宫使用矩阵表示,通路使用0表示,不通路使用1表示
三:记录每个路是否已经走过,若走过,则不再走
四:若某条路走不通,能回退到上一格,因此需要一个能保存走过的路的容器(即栈)

public interface Question {
    /**
     * 检查括号是否匹配
     * @param s
     * @return
     */
    boolean checkBrackets(String s);

    /**
     *  将十进制数a转换为r(r取[2,9])进制
     * @param a 需要转换的数
     * @param r 基数
     * @return
     */
    int convert(int a,int r);

    /**
     * 计算简单的算数表达式
     * @param str
     * @return
     */
    int CalculationArithmeticExpression(String str);

    /**
     * 输出n个布尔量所有可能的组合
     * @param b
     * @param k
     * @param n
     * @return
     */
    void outputCombinationByBoolean(int[] b,int k,int n);

    /**
     * 深度优先迷宫问题
     * @param maze 迷宫
     * @param move 方向矩阵
     */
    void SeekPath(int[][] maze, int[][] move);
}
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * 栈习题
 */
public class Solution implements Question{

    public static void main(String[] args){
        Solution test=new Solution();
        //   String s="{[((('())(')))]}";
        //  System.out.println(test.checkBrackets(s));

        //    System.out.println(test.convert(3425,8));
        //  String infix="10+(18+9*3)/15-6";
        //   System.out.println(test.CalculationArithmeticExpression(infix));
        // System.out.println(10+(18+9*3)/15-6);
        //      int n=3;
        //    int[] b=new int[n];
        //  int k=0;
        // test.outputCombinationByBoolean(b,k,n);
        int[][] map = {                           //迷宫地图,1代表墙壁,0代表通路
               {1,1,1,1,1,1,1,1,1,1,1,1},   //声明迷宫数组
                {1,0,0,0,0,0,1,0,0,0,1,1},
                {1,1,1,0,1,0,1,0,0,0,0,1},
                {1,1,1,1,1,1,1,1,1,1,1,1},

        };

        int[][] move={{0,1},{1,0},{0,-1},{-1,0}};
        test.SeekPath(map,move);
    }
    /**
     * 检查括号是否匹配
     * @param s
     * @return
     */
    @Override
    public boolean checkBrackets(String s) {
        if(s.length()==0)
            return false;
        //初始化栈
        Stack<Character> stack=new Stack<>();
        char c;
        for(int i=0;i<s.length();){
            c=s.charAt(i);
            //跳过单引号和双引号中间的内容
            if(((int)c)==39){
                //跳过单引号包含的内容
                i++;
                while ((c=s.charAt(i))!=39){
                    i++;
                }
                i++;
            }else if(((int)c)==34){
                //跳过双引号
                i++;
                while ((c=s.charAt(i))!=34){
                    i++;
                }
                i++;
            }else {
                switch (c) {
                    case '(':
                        stack.push(c);
                        i++;
                        break;
                    case '{':
                        stack.push(c);
                        i++;
                        break;
                    case '[':
                        stack.push(c);
                        i++;
                        break;
                    case ')':
                        if(stack.peek()=='('){
                            stack.pop();
                            i++;
                            break;
                        }else {
                            return false;
                        }
                    case '}':
                        if(stack.peek()=='{'){
                            stack.pop();
                            i++;
                            break;
                        }else {
                            return false;
                        }
                    case ']':
                        if(stack.peek()=='['){
                            stack.pop();
                            i++;
                            break;
                        }else {
                            return false;
                        }
                }
            }
        }
        if(stack.isEmpty())
            return true;
        return false;
    }

    /**
     * 将十进制数a转换为r(r取[2,9])进制
     * @param a 需要转换的数
     * @param r 基数
     * @return
     */
    @Override
    public int convert(int a, int r) {
        if(r<2 || r>9 || a<0)
            return -1;
        //初始化
        Stack<Integer> stack=new Stack<>();
        //商
        int quo;
        //余数
        int remainder;
        while ((quo=a/r)!=0){
            remainder=a%r;
            stack.push(remainder);
            a=quo;
        }
        //压入最后一项商等于0的余数
        remainder=a%r;
        stack.push(remainder);

        StringBuilder s=new StringBuilder();
        while (!stack.isEmpty()){
            s.append(stack.pop());
        }
        return Integer.parseInt(s.toString());
    }

    /**
     * 计算简单的算数表达式
     * 仅包含+、-、*、/
     * @param str
     * @return
     */
    @Override
    public int CalculationArithmeticExpression(String str) {
        //为简单起见,将str转为List
        List<String> list=new ArrayList<>();
        convertList(list,str);
        List<String> suffixList=infixChangedToSuffix(list);
        //负责计算
        Stack<Integer> stack=new Stack<>();
        String item;
        for(int i=0;i<suffixList.size();i++){
            item=suffixList.get(i);
            if(isNum(item)){
                stack.push(Integer.parseInt(item));
            }else {
                Integer v2=stack.pop();
                Integer v1=stack.pop();
                //注意运算的顺序
                if(item.equals(PriorityOperators.ADD.getOperators())){
                    stack.push(v1+v2);
                }else if(item.equals(PriorityOperators.SUBTRACT.getOperators())){
                    stack.push(v1-v2);
                }else if(item.equals(PriorityOperators.MULTIPLY.getOperators())){
                    stack.push(v1*v2);
                }else if(item.equals(PriorityOperators.DIVIDE.getOperators())){
                    stack.push(v1/v2);
                }
            }
        }
        return stack.pop();
    }

    /**
     * 将中缀表达式转换为List
     * @param list
     * @param str
     */
    private void convertList(List<String> list, String str) {
        int i=0;
        int start;
        while (i<str.length()){
            if(isNum(String.valueOf(str.charAt(i)))){
                start=i;
                while (i<str.length() && isNum(String.valueOf(str.charAt(i)))){
                    i++;
                }
                list.add(str.substring(start,i));
            }else {
                list.add(String.valueOf(str.charAt(i)));
                i++;
            }

        }
    }

    /**
     * 判断是否是数字
     * @param s
     * @return
     */
    private boolean isNum(String s) {
        try{
            Integer.parseInt(s);
            return true;
        }catch (Exception e){
            return false;
        }
    }

    /**
     * 为了方便计算机进行计算,只支持数字的简单计算
     * 将中缀表达式转换为后缀表达式
     * @param s1 中缀表达式
     * @return
     */
    private List<String> infixChangedToSuffix(List<String> s1){
        //负责装后缀表达式
        List<String> s2=new ArrayList<>();
        //负责装运算符,运算符有优先级
        Stack<PriorityOperators> r=new Stack<>();
        String c;
        int index=0;
        while (index<s1.size()){
            c=s1.get(index);
            if(isNum(c)){
                //数字
                s2.add(c);
            }else if(!c.equals(PriorityOperators.RIGHT.getOperators())){
                //运算符(除了右括号),则压栈,压栈前需判断优先级
                if(r.isEmpty()){
                    if(PriorityOperators.ADD.getOperators().equals(c)){
                        r.push(PriorityOperators.ADD);
                    }else if(PriorityOperators.SUBTRACT.getOperators().equals(c)){
                        r.push(PriorityOperators.SUBTRACT);
                    }else if(PriorityOperators.MULTIPLY.getOperators().equals(c)){
                        r.push(PriorityOperators.MULTIPLY);
                    }else if(PriorityOperators.DIVIDE.getOperators().equals(c)){
                        r.push(PriorityOperators.DIVIDE);
                    }else {
                        r.push(PriorityOperators.LEFT);
                    }
                }else {
                    //判断当前的运算符优先级与栈顶运算符优先级
                    if(PriorityOperators.ADD.getOperators().equals(c)){
                        if(PriorityOperators.ADD.getPriority()>r.peek().getPriority()){
                            //当前优先级大于栈顶元素优先级,则直接压栈
                            r.push(PriorityOperators.ADD);
                        }else {
                           //弹栈,直到当前当前优先级大于栈顶元素优先级,才结束循环
                            while (!r.isEmpty() && (PriorityOperators.ADD.getPriority()<r.peek().getPriority())){
                                s2.add(r.pop().getOperators());
                            }
                            r.push(PriorityOperators.ADD);
                        }
                    }else if(PriorityOperators.SUBTRACT.getOperators().equals(c)){
                        if(PriorityOperators.SUBTRACT.getPriority()>r.peek().getPriority()){
                            //当前优先级大于栈顶元素优先级,则直接压栈
                            r.push(PriorityOperators.SUBTRACT);
                        }else {
                            //弹栈,直到当前当前优先级大于栈顶元素优先级
                            while (!r.isEmpty() && (PriorityOperators.SUBTRACT.getPriority()<r.peek().getPriority())){
                                s2.add(r.pop().getOperators());
                            }
                            r.push(PriorityOperators.SUBTRACT);
                        }

                    }else if(PriorityOperators.MULTIPLY.getOperators().equals(c)){
                        if(PriorityOperators.MULTIPLY.getPriority()>r.peek().getPriority()){
                            //当前优先级大于栈顶元素优先级,则直接压栈
                            r.push(PriorityOperators.MULTIPLY);
                        }else {
                            //弹栈,直到当前当前优先级大于栈顶元素优先级
                            while (!r.isEmpty() && (PriorityOperators.MULTIPLY.getPriority()<r.peek().getPriority())){
                                s2.add(r.pop().getOperators());
                            }
                            r.push(PriorityOperators.MULTIPLY);
                        }

                    }else if(PriorityOperators.DIVIDE.getOperators().equals(c)){
                        if(PriorityOperators.DIVIDE.getPriority()>r.peek().getPriority()){
                            //当前优先级大于栈顶元素优先级,则直接压栈
                            r.push(PriorityOperators.DIVIDE);
                        }else {
                            //弹栈,直到当前当前优先级大于栈顶元素优先级
                            while (!r.isEmpty() && (PriorityOperators.DIVIDE.getPriority()<r.peek().getPriority())){
                                s2.add(r.pop().getOperators());
                            }
                            r.push(PriorityOperators.DIVIDE);
                        }
                    }else {
                        //左括号
                        r.push(PriorityOperators.LEFT);
                    }
                }
            }else {
                //遇到右括号,出栈,直到左括号
                while (!r.isEmpty() && (!r.peek().getOperators().equals(PriorityOperators.LEFT.getOperators()))){
                    s2.add(r.pop().getOperators());
                }
                if(!r.isEmpty())
                    //弹出左括号
                    r.pop();

            }
            index++;
        }
        while (!r.isEmpty())
            s2.add(r.pop().getOperators());
        return s2;
    }

    @Override
    public void outputCombinationByBoolean(int[] b,int k,int n) {
        if(k==n){
            System.out.print(Arrays.toString(b));
        }else {
            b[k]=1; outputCombinationByBoolean(b,k+1,n);
            b[k]=0; outputCombinationByBoolean(b,k+1,n);
        }
    }
    /**
     * 深度优先搜索迷宫问题(回溯、递归思路都一样)
     * @param maze 迷宫
     * @param move 方向矩阵
     *  核心思想类似于双指针,例如:
    下标 0,1,2,3,4,5,6,7,8,9,10,11
     * 0{1,1,1,1,1,1,1,1,1,1,1,1},
     * 1{1,0,0,0,0,0,1,0,0,0,1,1},
     * 2{1,1,1,0,1,0,1,0,0,0,0,1},
     * 3{1,1,1,1,1,1,1,1,1,1,1,1}
     *  过程是:一个指针p指向当前点,即(x,y),另一个指针q指向当前点的d方向上的点,即(g,h)
     *  初始情况:x=1,y=1,d=0,因为(1,1)在0方向的点是(1,2),即g=1,h=2,maze[g][h]!=1 && mark[g][h]!=1 =>(1,2)可达
     *  因此将(1,1,0)压栈;当前点向后挪一位,且将d赋值为0,即(x=g;y=h;d=0;),之后同理
     *  压栈过程:(1,1,0)-->(1,2,0)-->(1,3,0)-->(1,4,0)-->(1,5,1),注意(2,5,d)点(q指针指向的点)不压栈,因为d=0,1,2,3都不可达
     *  当都不可达(q指针指向的点)时,弹栈(p指针指向的点),即弹出(1,5,1),d++,即d=2,换个方向重复上述过程
     */
    @Override
    public void SeekPath(int[][] maze,int[][] move) {
        if (maze==null || move==null)
            return;

        int m=maze.length-1;
        int n=maze[0].length-1;
        //用于标记节点是否已经访问过,访问过则标记1
        int[][] mark=new int[m+1][n+1];
        //用于存放走过的路径
        Stack<Node> stack=new Stack<>();

        //初始点默认是没有方向的(因为d=-1)
        stack.push(new Node(1,1,-1));

        //因为maze外围会人为增加墙,所以初始点是(1,1)
        //增加墙的原因是避免判断边界,代码具有统一性
        mark[1][1]=1;
       //核心思想是:类似于双指针,例如,当前点(1,1)以方向0出发,
       //下一点是(1,2),若可达,则把(1,1,0)压栈
       //若(1,2)不可达,说明(1,1)点的0方向不可达,则d++,尝试1方向
       //若4个方向都不可达,则当前点弹栈,d++,换个方向继续尝试
        Node root;
        while (!stack.isEmpty()){
             //当前点的4个方向都不可达(即d<0或d>3),弹出当前点
             root=stack.pop();
            //d++,换个方向继续尝试
             int x=root.getX(), y=root.getY(), d=root.getDirection()+1;
             while (d<4){
                 if((x==(m-1)) && y==(n-1)){
                     //出口,输出一条路径
                     System.out.println(x+","+y);
                     Node h;
                     while (!stack.isEmpty()){
                         h=stack.pop();
                         System.out.println(h.getX()+","+h.getY());
                     }
                     return;
                 }
                 //maze[x][y]是当前点,maze[g][h]是当前点(maze[x][y])在d方向上的点
                 int g=x+move[d][0],h=y+move[d][1];
                  //用于判断当前节点d方向的后一个节点是否可达
                 if(maze[g][h]==0 && mark[g][h]==0){
                     //若可达
                     mark[g][h]=1;
                    //压入当前节点和方向d(d的方向其实是maze[g][h]相对于maze[x][y]的方向)
                     stack.push(new Node(x,y,d));
                     x=g;y=h;d=0;
                 }else {
                     //maze[g][h]不可达,则换个方向测试
                       d++;
                 }
             }
        }
        System.out.println("no path");
    }



    /**
     * 用于封装迷宫的结点
     */
    static class Node{
        /**
         * 横坐标
         */
        private int x=-1;
        /**
         * 纵坐标
         */
        private int y=-1;
        /**
         * 方向,取值0,1,2,3,分别表示E、S、W、N
         */
        private int direction=-1;


        public Node(){}
        public Node(int x, int y,int direction) {
            this.x = x;
            this.y = y;
            this.direction=direction;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public int getDirection() {
            return direction;
        }


        public void setX(int x) {
            this.x = x;
        }

        public void setY(int y) {
            this.y = y;
        }

        public void setDirection(int direction) {
            this.direction = direction;
        }


        @Override
        public String toString() {
            return "Node{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }

    }

    /**
     * 封装有优先级的运算符
     * 优先级:
     * +  2
     * -  1
     * *  4
     * /  3
     */
    enum PriorityOperators{
        ADD("+",2),
        SUBTRACT("-",1),
        MULTIPLY("*",4),
        DIVIDE("/",3),
        LEFT("(",0),
        RIGHT(")",0);
        //运算符
        private String operators;
        //优先级
        private int priority;

        PriorityOperators(String operators, int priority) {
            this.operators = operators;
            this.priority = priority;
        }

        public String getOperators() {
            return operators;
        }

        public int getPriority() {
            return priority;
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。