代码随想录算法训练营_day14

题目信息226. 翻转二叉树

  • 题目链接: https://leetcode.cn/problems/invert-binary-tree/description/
  • 题目描述:给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

解法一: {{递归法}}

解题思路

翻转 ——> 把每一个节点的左右孩子交换一下就可以
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!

层序遍历依然可以!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的

对于二叉树的递归法的前中后序遍历,已经在二叉树:前中后序递归遍历 (opens new window)详细讲解了。

我们下文以前序遍历为例,通过动画来看一下翻转的过程:

翻转二叉树

代码实现

public TreeNode invertTree(TreeNode root) {  
    if (root == null){  
        return null;  
    }  
    swapChildren(root);  
    invertTree(root.left);  
    invertTree(root.right);  
    return root;  
  
}  
private void swapChildren(TreeNode root){  
    TreeNode tmp = root.left;  
    root.left = root.right;  
    root.right = tmp;  
}

解法二: {{迭代法}}

解题思路

  • 首先,检查根节点是否为null,如果是,则直接返回null。
  • 创建一个双端队列deque,将根节点加入队列。
  • 使用while循环,当队列不为空时:
    1. 获取当前层的节点数量size。
    2. 对当前层的每个节点:
      • 从队列中取出一个节点。
      • 交换这个节点的左右子节点。
      • 如果左子节点不为null,将其加入队列。
      • 如果右子节点不为null,将其加入队列。

代码实现

    public TreeNode invertTree(TreeNode root) {
        if (root == null) {return null;}
        ArrayDeque<TreeNode> deque = new ArrayDeque<>();
        deque.offer(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            while (size-- > 0) {
                TreeNode node = deque.poll();
                swap(node);
                if (node.left != null) deque.offer(node.left);
                if (node.right != null) deque.offer(node.right);
            }
        }
        return root;
    }

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

整个表达式 size-- > 0 的执行顺序是:

  • 首先,检查 size 是否大于0
  • 如果大于0,则条件为真,并且 size 的值减1
  • 如果不大于0,则条件为假,循环结束

题目信息101. 对称二叉树

  • 题目链接: https://leetcode.cn/problems/symmetric-tree/description/
  • 题目描述:给你一个二叉树的根节点 root , 检查它是否轴对称。

解法一: {{递归法}}

解题思路

  1. 确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是bool类型。

  private boolean compare(TreeNode left, TreeNode right) 
  1. 确定终止条件

要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

  • 左节点为空,右节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  • 左右都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

if (left == null && right != null) {  
    return false;  
}  
if (left != null && right == null) {  
    return false;  
}  
if (left == null && right == null) {  
    return true;  
}  
if (left.val != right.val) {  
    return false;  
}
  1. 确定单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。
 // 比较外侧
  boolean compareOutside = compare(left.left, right.right);
 // 比较内侧
 boolean compareInside = compare(left.right, right.left);
 return compareOutside && compareInside;

代码实现

    /**
     * 递归法
     */
    public boolean isSymmetric1(TreeNode root) {
        return compare(root.left, root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {

        if (left == null && right != null) {
            return false;
        }
        if (left != null && right == null) {
            return false;
        }

        if (left == null && right == null) {
            return true;
        }
        if (left.val != right.val) {
            return false;
        }
        // 比较外侧
        boolean compareOutside = compare(left.left, right.right);
        // 比较内侧
        boolean compareInside = compare(left.right, right.left);
        return compareOutside && compareInside;
    }

题目信息 104. 二叉树的最大深度

  • 题目链接: https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/
  • 题目描述: 给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数

解法一: {{递归法}}

解题思路

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。

这一点其实是很多同学没有想清楚的,很多题解同样没有讲清楚。

我先用后序遍历(左右中)来计算树的高度。

  1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
int maxDepth(TreeNode root)
  1. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。

if (root == null){
return 0;
}

3. 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
```java
int leftDepth = maxDepth(root.left);  
int rightDepth = maxDepth(root.right);  
return Math.max(leftDepth,rightDepth) + 1;

代码实现

    /**
     * 递归法
     */
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
    }

本题当然也可以使用前序,代码如下:(充分表现出求深度回溯的过程)

/**  
 * 递归法(求深度法)  
 *///定义最大深度  
int maxnum = 0;  
  
public int maxDepth(TreeNode root) {  
    ans(root,0);  
    return maxnum;  
}  
  
//递归求解最大深度  
void ans(TreeNode tr,int tmp){  
    if(tr==null) return;  
    tmp++;  
    maxnum = maxnum<tmp?tmp:maxnum;  
    ans(tr.left,tmp);  
    ans(tr.right,tmp);  
    tmp--;  
}

解法二: {{迭代法}}

解题思路

使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。

在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:

层序遍历

所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。

代码实现

/**  
 * 迭代法,使用层序遍历  
 */  
public int maxDepth(TreeNode root) {  
    if(root == null) {  
        return 0;  
    }  
    Deque<TreeNode> deque = new LinkedList<>();  
    deque.offer(root);  
    int depth = 0;  
    while (!deque.isEmpty()) {  
        int size = deque.size();  
        depth++;  
        for (int i = 0; i < size; i++) {  
            TreeNode node = deque.poll();  
            if (node.left != null) {  
                deque.offer(node.left);  
            }  
            if (node.right != null) {  
                deque.offer(node.right);  
            }  
        }  
    }  
    return depth;  
}

题目信息111. 二叉树的最小深度

  • 题目链接: https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/
  • 题目描述:给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

**说明:**叶子节点是指没有子节点的节点。

解法一: {{递归法}}

解题思路

本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始)

那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。

以下讲解中遍历顺序上依然采用后序遍历(因为要比较递归返回之后的结果,本文我也给出前序遍历的写法)。

本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:

111.二叉树的最小深度

这就重新审题了,题目中说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点

什么是叶子节点,左右孩子都为空的节点才是叶子节点

递归法

来来来,一起递归三部曲:

  1. 确定递归函数的参数和返回值

参数为要传入的二叉树根节点,返回的是int类型的深度。

代码如下:

int getDepth(TreeNode* node)

1

  1. 确定终止条件

终止条件也是遇到空节点返回0,表示当前节点的高度为0。

代码如下:

if (node == NULL) return 0;

1

  1. 确定单层递归的逻辑

这块和求最大深度可就不一样了,一些同学可能会写如下代码:

int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);
return result;

这个代码就犯了此图中的误区:

111.二叉树的最小深度

如果这么求的话,没有左孩子的分支会算为最短深度。

所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。

反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。

代码实现

/**  
 * 递归法,相比求MaxDepth要复杂点  
 * 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量  
 */  
public int minDepth(TreeNode root) {  
    if (root == null) {  
        return 0;  
    }  
    int leftDepth = minDepth(root.left);  
    int rightDepth = minDepth(root.right);  
    if (root.left == null) {  
        return rightDepth + 1;  
    }  
    if (root.right == null) {  
        return leftDepth + 1;  
    }  
    // 左右结点都不为null  
    return Math.min(leftDepth, rightDepth) + 1;  
}
/**  
 * 递归法(思路来自二叉树最大深度的递归法)  
 * 该题求最小深度,最小深度为根节点到叶子节点的深度,所以在迭代到每个叶子节点时更新最小值。  
 */  
int depth = 0;  
// 定义最小深度,初始化最大值  
int minDepth = Integer.MAX_VALUE;  
public int minDepth(TreeNode root) {  
    dep(root);  
    return minDepth == Integer.MAX_VALUE ? 0 : minDepth;  
}  
void dep(TreeNode root){  
    if(root == null) return ;  
    // 递归开始,深度增加  
    depth++;  
    dep(root.left);  
    dep(root.right);  
    // 该位置表示递归到叶子节点了,需要更新最小深度minDepth  
    if(root.left == null && root.right == null)  
        minDepth = Math.min(minDepth , depth);  
    // 递归结束,深度减小  
    depth--;  
}

解法二: {{迭代法,层序遍历}}

解题思路

需要注意的是,只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点

代码实现

/**  
 * 迭代法,层序遍历  
 */  
public int minDepth(TreeNode root) {  
    if (root == null) {  
        return 0;  
    }  
    Deque<TreeNode> deque = new LinkedList<>();  
    deque.offer(root);  
    int depth = 0;  
    while (!deque.isEmpty()) {  
        int size = deque.size();  
        depth++;  
        for (int i = 0; i < size; i++) {  
            TreeNode poll = deque.poll();  
            if (poll.left == null && poll.right == null) {  
                // 是叶子结点,直接返回depth,因为从上往下遍历,所以该值就是最小值  
                return depth;  
            }  
            if (poll.left != null) {  
                deque.offer(poll.left);  
            }  
            if (poll.right != null) {  
                deque.offer(poll.right);  
            }  
        }  
    }  
    return depth;  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值