树的搜索

80 阅读2分钟

前序遍历

对当前节点值进行操作,递归左子树,递归右子树。

中序遍历

递归左子树,对当前节点值进行操作,递归右子树。

后序遍历

递归左子树,递归右子树,对当前节点值进行操作。

层序遍历

使用队列Queue,将每一次节点放入queue中,通过遍历queue,每次从头到尾弹出一个节点,然后将下一层节点加入queue中,同时将弹出节点的value值放入需要返回的list集合中。(BFS)

出度入度

出了头结点,其他节点入度均为1,除了尾结点,其他节点出度均为1。可以用来判断序列化是否满足数的结构。 leetCod-331

public class isValidSerialization_331 {
    public static void main(String[] args) {
        System.out.println(isValidSerialization("#,7,6,9,#,#,#"));
    }
    private static boolean isValidSerialization(String preorder) {
        int in = 0,out = 0;
        String[] ss = preorder.split(",");
        for (int i = 0; i < ss.length; i++) {
            //只有数字有出度,并且每个数字对应出度为2
            if(!ss[i].equals("#")) out+=2;
            //根节点没有入度,其他每个值入度为1
            if(i!=0) in++;
            //如果未到最后一个,出度提前大于入度
            if(i !=(ss.length-1) && out<=in) return false;
        }
        return in==out;
    }
}

图的遍历

leetcode第778题 找到能将开始和结尾连通的最小根节点

public class swimInWater_778 {
    public static void main(String[] args) {
        int[][] grid = {
                {0,1,2,3,4},
                {24,23,22,21,5},
                {12,13,14,15,16},
                {11,17,18,19,20},
                {10,9,8,7,6}};
        System.out.println(swimInWater1(grid));
        System.out.println(swimInWater2(grid));
    }

    //解法一:Kruskal算法
    //定义三个函数:1、链接函数,将x,y两个点设置为同一根节点。2、判断是否连通。3、找到根节点
    static int n;
    static int[] p;
    private static void union(int x,int y){
        //将x,y的根节点进行合并
        p[find(x)] = p[find(y)];
    }

    private static boolean query(int x,int y){
        //判断x,y是否在同一个根节点
        return find(x) == find(y);
    }

    //找到x的根节点
    private static int find(int x){
        if(p[x] != x){
            p[x] = find(p[x]);
        }
        return p[x];
    }
    public static int swimInWater1(int[][] grid) {
        n = grid.length;
        //初始化并查集
        p = new int[n * n];
        for (int i = 0; i < n*n; i++) {
            p[i] = i;
        }
        //预处理边
        List<int[]> edges = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int index = getIndex(i, j);
                //将连通的边放入集合中
                p[index] = index;
                if(i+1<n){
                    int a = index;
                    int b = getIndex(i+1,j);
                    int w = Math.max(grid[i][j],grid[i+1][j]);
                    edges.add(new int[]{a,b,w});
                }
                if(j+1<n){
                    int a = index;
                    int b = getIndex(i,j+1);
                    int w = Math.max(grid[i][j],grid[i][j+1]);
                    edges.add(new int[]{a,b,w});
                }
            }
        }

        //对边的权值进行排序
        Collections.sort(edges,(a,b)->a[2]-b[2]);
        //从最小权值的边开始进行并查集
        int start = getIndex(0,0),end = getIndex(n-1,n-1);
        for(int[] edge : edges){
            int a = edge[0],b = edge[1],w = edge[2];
            union(a,b);
            if(query(start,end)){
                return w;
            }
        }
        return 0;
    }
    //将二维坐标映射到一维,方便点的表示
    private static int getIndex(int i, int j) {
        return i * n + j;
    }

    //解法二:BFS+二分
    public static int swimInWater2(int[][] grid){
        int len = grid.length;
        int left = 0,right = len*len-1;
        while (left < right){
            int mid = (left+right)>>1;
            if(check(grid,mid)){
                right = mid;
            }else {
                left = mid+1;
            }
        }
        return right;
    }

    private static boolean check(int[][] grid,int time){
        int len = grid.length;
        boolean[][] isVisited = new boolean[len][len];
        int[][] direction = {{0,1},{1,0},{0,-1},{-1,0}};
        Deque<int[]> deque = new LinkedList<>();
        deque.add(new int[]{0,0});
        isVisited[0][0] = true;
        while (!deque.isEmpty()){
            int[] from = deque.pollLast();
            int x = from[0],y = from[1];
            if(x== len-1 && y==len-1) return true;
            for (int[] dir : direction){
                int newX = x+dir[0],newY = y+dir[1];
                int[] to = new int[]{newX,newY};
                if(inArea(len,newX,newY) && !isVisited[newX][newY]
                        && canMove(grid,from,to,time)){
                    isVisited[newX][newY] = true;
                    deque.addLast(to);
                }
            }
        }
        return false;
    }

    private static boolean inArea(int len,int x,int y){
        return (x>=0 && x<len && y>=0 && y<len);
    }

    private static boolean canMove(int[][]grid,int[] from,int[] to,int time){
        return time>=(Math.max(grid[from[0]][from[1]],grid[to[0]][to[1]]));
    }
}

掌握递归与非递归两种遍历树的方法

非递归方法一般使用队列,根据需要使用选择使用队列和双端对列,看此时节点的弹出需要从首段还是尾端,一般建议选择双端队列。