代码随想录算法训练营12

226.翻转二叉树

整颗二叉树翻转 -> 从下往上依次翻转子树的左右节点
结合之前翻转字符串哪些,我总感觉这些翻转一类的题目就是局部翻转最终形成整体翻转
这道题主要就是要理解递归法为什么不能用中序
我把递归的方法invertTree(Node)的含义写作:交换以Node为根节点的树的左右节点
那么假如中序遍历可以,那么伪代码应该就是

invertTree(node.left)
swap(node.left, node.right) 
// 由于上一行交换了左右子树,因此此时的node的右子树就是原本的node.left
invertTree(node.right) // 因此这一行就是把原本的node的左子树又翻转了一遍,但是我们原意是翻转原本的node的右子树
/**
 * Definition for a binary tree node.
 * public 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 {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        invert(root);
        return root;
    }

    public  void invert (TreeNode node) {
        if (node == null) return;
        swap(node);
        invert(node.left);
        invert(node.right);
    }

    public void swap (TreeNode node) {
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

101. 对称二叉树

我把递归函数定义为compare(node1, node2),这个递归函数的含义是检查以node1为根节点的子树是否和以node2为根节点的子树对称
什么叫对称?左子树的右边和右子树的左边一样,左子树的左边和右子树的右边一样
因此需要用后序遍历,因为只有知道了左子树、右子树分别是否对称,才能进一步判断这棵树是否整体对称
我觉得这个递归函数和一般的递归函数有一点点不同,不同之处在于这个递归函数是有返回值的,因此我觉得递归分两步:

  • 确定入参
  • 如何得到这个返回值

在这里compare的入参已经确定,现在就是要考虑怎么得到这个返回值。compare(node1, node2)是判断两棵树是否对称,所以可以枚举所有的情况来判断是否对称:

  • 如果node1和node2都是空,则对称,返回true
  • 如果node1是空,但是node2不是空,则返回false
  • 如果node1不是空,但是node2是空,则返回false
  • 如果node1的值和node2的值不相等,相当于两棵树的根节点不相等,那肯定就是不对称,返回false
  • 如果node1的值 == node2的值
    • 如果compare(node1.left, node2.right) 的返回值是true,说明node1的左子树和node2的右子树对称
    • 如果compare(node1.right, node2.left) 的返回值是true,说明node1的右子树和node2的左子树对称
    • 只有上面两个都是true,才说明node1和node2的子树都对称,再加上node1的值 == node2的值,那么就说明整个都对称,返回true
/**
 * Definition for a binary tree node.
 * public 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 {
    public boolean isSymmetric(TreeNode root) {
        boolean res = compare(root.left, root.right);
        return res;
    }

    public boolean compare(TreeNode node1, TreeNode node2) {
        if (node1 == null && node2 == null) return true;
        else if (node1 != null && node2 == null) return false;
        else if (node1 == null && node2 != null) return false;
        else if (node1.val != node2.val) return false;
        
        // 此时node1.val == node2.val
        boolean isInnerSymetric = compare(node1.right, node2.left);
        boolean isOuterSymetric = compare(node1.left, node2.right);
        return isOuterSymetric && isInnerSymetric;
    }
}

我感觉这题层序遍历也能做,每一层取第一个和最后一个比较一下,一层一层比

104.二叉树的最大深度

首先分清二叉树节点的高度和深度:

  • 高度是从下往上,也就是从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 深度是从上往下,也就是从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

如果用递归法:

  • 求深度用前序遍历(要带上回溯)
  • 求高度是用后序遍历,因为只有知道了左右子树的高度,才能确定根节点的高度

我感觉选择用什么遍历方式取决于处理中间节点的时候需不需要左右节点的信息、处理左右节点的时候需不需要中间节点的信息

有一个特例,一棵树的根节点的高度和深度相等,由于本题要求最大深度,其实就是求根节点的深度,所以可以转换成求根节点的高度

考虑怎么写递归,递归函数是getHeight(node),有返回值,返回的是node的深度
因此现在就是要把所有会导致return的情况都枚举出来:

  • 如果node是空,return 0
  • 如果node不是空
    • 调用getHeight(node.left),获取左子树的高度leftHeight
    • 调用getHeight(node.right),获取右子树的高度rightHeight
    • node的高度 = max(leftHeight, rightHeight) + 1
    • return node的高度
class Solution {
    public int maxDepth(TreeNode root) {
        int res = getHeight(root);
        return res;
    }

    public int getHeight(TreeNode node) {
        if (node == null) return 0;
        int leftHeight = getHeight(node.left);
        int rightHeight = getHeight(node.right);
        int height = Math.max(leftHeight, rightHeight) + 1;
        return height;
    }
}

当然这道题要使用层序遍历就很好理解了

111.二叉树的最小深度

把最小深度转换为最小高度,依旧使用后序遍历。

递归函数是getMinHeight(node),含义是返回node节点的最小高度。那么什么情况下会返回最小高度:

  • 如果node=null,return 0
  • 如果node.left == null && node.right != null,调用getMinHeight(node.right)获取右子树的最小深度rightMinHeight,return rightMinHeight + 1
  • 如果node.left != null && node.right == null,调用getMinHeight(node.left)获取左子树的最小深度leftMinHeight,return leftMinHeight + 1
  • 如果node.left != null && node.right != null,return min(leftMinHeight, rightMinHeight) + 1

主要就是需要理解左右子树一个为空一个部位空的处理逻辑,可以看下图理解

image.png

class Solution {
    public int minDepth(TreeNode root) {
        int res = getMinHeight(root);
        return res;
    }
    
    public int getMinHeight(TreeNode node) {
        if (node == null) {
            return 0;
        } else if (node.left == null && node.right != null) {
            int rightMinHeight = getMinHeight(node.right);
            return rightMinHeight + 1;
        } else if (node.left != null && node.right == null) {
            int leftMinHeight = getMinHeight(node.left);
            return leftMinHeight + 1;
        } else {
            int rightMinHeight = getMinHeight(node.right);
            int leftMinHeight = getMinHeight(node.left);
            return Math.min(leftMinHeight, rightMinHeight) + 1;
        }
    }
}

同样用层序遍历更方便理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值