深度优先遍历 DFS

题目均来自leetcode,版权为leetcode所有

练习:

1. 平衡二叉树

实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两棵子树的高度差不超过 1。

//二叉树节点定义
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

class Test {
    public boolean isBalanced(TreeNode root) {
        if(root == null){
            return true;
        }
        int left_height = height(root.left);
        int right_height = height(root.right);
        if(Math.abs(left_height - right_height) > 1){
            return false;
        }
        else{
            return isBalanced(root.left) && isBalanced(root.right);
            //必须满足左右同时成立才能考察树的平衡性
        }
    }

    public int height(TreeNode root){
        if(root == null){
            return 0;
        }
        
        //递归地获取某节点所在高度
        int left_height = height(root.left);
        int right_height = height(root.right);

        if(left_height >= right_height){
            return 1+ left_height;
        }
        else{
            return 1 + right_height;
        }
    }
}

执行用时:1 ms

内存消耗:38.4 MB

2. 叶子相似的树

一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。如果两棵树的叶值序列相同,则认为两棵树是叶子相似的树。给定两颗树,判断它们是否为叶子相似的树。

思路:一个非叶子节点有大于等于1个孩子,无法用遍历并返回数值来记录所有叶子。于是在递归遍历的同时记录叶子节点的值。

import java.util.ArrayList;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

class Test {
    private ArrayList a = new ArrayList(200);
    private ArrayList b = new ArrayList(200);

    public boolean leafSimilar(TreeNode root1, TreeNode root2) {
        getLeaf(root1, a);
        getLeaf(root2, b);

        if(a.equals(b)){
            return true;
        }
        else{
            return false;
        }
    }

    public void getLeaf(TreeNode root, ArrayList record){
        if(root == null){
            return;
        }
        if(root.left == null && root.right == null){
            record.add(root.val);
            return;
        }
        else if(root.left == null){
            getLeaf(root.right, record);
        }
        else if(root.right == null){
            getLeaf(root.left, record);
        }
        else {
            getLeaf(root.left, record);
            getLeaf(root.right, record);
        }
    }
}

执行用时:0 ms

内存消耗: 36.2 MB

3. 水域大小

你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。

import java.util.Arrays;

class Test {
    int[] dirX = {0, 1, 0, -1, 1, 1, -1, -1};
    int[] dirY = {1, 0, -1, 0, 1, -1, 1, -1};

    public int[] pondSizes(int[][] land) {
        int[] result = new int[1];
        int x = 0;
        int y = 0;
        for(int i=0;i<land.length;i++){
            for(int j=0;j<land[0].length;j++){
                if(land[i][j] == 0){
                    //这两部实际上是以便添加数据以便扩展长度,实际是向数组尾部追加数据
                    result[result.length-1] = dfs(land,i,j);
                    result=Arrays.copyOf(result, result.length+1);
                }
            }
        }
        //去数组末尾
        result=Arrays.copyOf(result, result.length-1);
        Arrays.sort(result);
        return result;
    }

    public int dfs(int[][] land, int x, int y){
        int count = 0;
        if(land[x][y] == 0){
            count++;
            land[x][y] = 10;//用10表示该点已经遍历过了
        }
        for(int i = 0;i<8;i++){
            int tx = x + dirX[i];
            int ty = y + dirY[i];
            if(tx<0||ty<0||tx>=land.length||ty>=land[0].length||land[tx][ty] != 0){
                continue;
            }
            count += dfs(land,tx,ty);
        }
        return count;
    }
}

执行用时:65ms

内存消耗:77.2MB

4. 省份数量(邻接表dfs)

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

class Test {
    public int findCircleNum(int[][] isConnected){
        boolean[] marked = new boolean[isConnected.length];
        int result = 0;
        for(int i=0;i<isConnected.length;i++){
            if(!marked[i]){
                dfs(isConnected, marked, i);
                result++;
            }
        }

        return result;
    }

    //找到所有和点x相邻的点
    public void dfs(int[][] isConnected, boolean[] marked, int x){
        for(int i=0;i<isConnected.length;i++){
            if(isConnected[x][i] == 1 && x != i && !marked[i]){
                marked[i] = true;
                dfs(isConnected, marked, i);
            }
        }
    }
}

执行用时:1ms

内存消耗:39.2MB

5. 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树

思路1:最先出现的递归调用会不断地把程序递出,而最后出现的递归会在归来的过程中递出。所以与本题类似的问题,当在递归过程中会不定时出现分枝,可以使用两个递归,前一个是一定会发生的递归,后一个是分枝的递归。(本题解来自leetcode)

//Definition for a binary tree node.
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
class Solution {
    int sum = 0;
    public TreeNode convertBST(TreeNode root) {
        if(root != null){
            convertBST(root.right);
            sum += root.val;
            root.val = sum;
            if(root.left != null){
                convertBST(root.left);
            }
        }
        return root;
    }
}

执行用时:0 ms

内存消耗:39 MB

思路2:累加的过程是中序遍历的反过程,即反序中序遍历,可以使用Morris遍历的方法:利用树的大量空闲指针,减少内存开销。规则如下:

1.如果当前节点的右子节点为空,处理当前节点,并遍历当前节点的左子节点;

  • 如果当前节点的右子节点不为空,找到当前节点右子树的最左节点(该节点为当前节点中序遍历的前驱节点);

  • 如果最左节点的左指针为空,将最左节点的左指针指向当前节点,遍历当前节点的右子节点;

  • 如果最左节点的左指针不为空,将最左节点的左指针重新置为空(恢复树的原状),处理当前节点,并将当前节点置为其左节点;

重复步骤 1 和步骤 2,直到遍历结束。(本题解来自leetcode)

class Solution {
    public TreeNode convertBST(TreeNode root) {
        int sum = 0;
        TreeNode node = root;

        while (node != null) {
            //规则1
            if (node.right == null) {
                sum += node.val;
                node.val = sum;
                node = node.left;
            } else {
                TreeNode succ = getSuccessor(node);
                if (succ.left == null) {
                    succ.left = node;
                    node = node.right;
                } else {
                    succ.left = null;
                    sum += node.val;
                    node.val = sum;
                    node = node.left;
                }
            }
        }

        return root;
    }

    public TreeNode getSuccessor(TreeNode node) {
        TreeNode succ = node.right;
        while (succ.left != null && succ.left != node) {
            succ = succ.left;
        }
        return succ;
    }
}

执行用时:1 ms

内存消耗: 38.6 MB

6. 求根节点到叶节点的数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

  • 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。

计算从根节点到叶节点生成的 所有数字之和 。

class Solution {
    public int sumNumbers(TreeNode root) {
        return dfs(root,0);
    }

    public int dfs(TreeNode root, int prevSum){
        if(root == null){
            return 0;
        }
        int sum = prevSum*10 + root.val;
        //由于前面有节点为空则返回0的声明,所以可以写在一起;
        if(root.left != null || root.right != null){
            sum = dfs(root.left, sum) + dfs(root.right, sum);
        }
        return sum;
    }
}

执行用时:0 ms

内存消耗:35.7 MB

7. 找树左下角的值

给定一个二叉树,在树的最后一行找到最左边的值。

思路:和第5题的思路1相同,因为所求结果与左右有关,故在dfs函数中递归调用两次,后一次的递送方向与最终所求(左)是相同的。

class Solution {
    int deepest = 0;
    int result = 0;
    
    public int findBottomLeftValue(TreeNode root) {
        dfs(root, 1);
        return result;
    }

    public void dfs(TreeNode root, int floor){
        if(root == null){
            return;
        }
        if(root.right != null){
            dfs(root.right, floor + 1);
        }
        if(deepest <= floor){
                deepest = floor;
                result = root.val;
            }
        if(root.left != null){
            dfs(root.left, floor + 1);
        }
    }
}

执行用时:0 ms

内存消耗:37.9 MB

(未完,待更新)

posted @ 2021-03-17 21:33  PaB式乌龙茶  阅读(71)  评论(0编辑  收藏  举报