牛客题解 | 二叉树的镜像

题目## 题目

题目链接

题目的主要信息:
  • 将二叉树镜像,即将其所有左右子树交换

我们可以考虑自底向上依次交换二叉树的左右节点。

举一反三:

学习完本题的思路你可以解决如下题目:

BM28. 二叉树的最大深度

BM29. 二叉树中和为某一值的路径(一)

BM31. 对称的二叉树

BM32. 合并二叉树

BM36. 判断是不是平衡二叉树

BM39. 序列化二叉树

BM41. 输出二叉树的右视图

方法一:递归(推荐使用)

知识点:二叉树递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。

思路:

因为我们需要将二叉树镜像,意味着每个左右子树都会交换位置,如果我们从上到下对遍历到的节点交换位置,但是它们后面的节点无法跟着他们一起被交换,因此我们可以考虑自底向上对每两个相对位置的节点交换位置,这样往上各个子树也会被交换位置。

自底向上的遍历方式,我们可以采用后序递归的方法。

具体做法:

  • step 1:先深度最左端的节点,遇到空树返回,处理最左端的两个子节点交换位置。
  • step 2:然后进入右子树,继续按照先左后右再回中的方式访问。
  • step 3:再返回到父问题,交换父问题两个子节点的值。

Java实现代码:

import java.util.*;
public class Solution {
    public TreeNode Mirror (TreeNode pRoot) {
        //空树返回
        if(pRoot == null) 
            return null;
        //先递归子树
        TreeNode left = Mirror(pRoot.left);  
        TreeNode right = Mirror(pRoot.right);
        //交换
        pRoot.left = right; 
        pRoot.right = left;
        return pRoot;
    }
}

C++实现代码:

class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        //空树返回
        if(pRoot == NULL) 
            return NULL;
        //先递归子树
        TreeNode* left = Mirror(pRoot->left);  
        TreeNode* right = Mirror(pRoot->right);
        //交换
        pRoot->left = right; 
        pRoot->right = left;
        return pRoot;
    }
};

Python实现代码

class Solution:
    def Mirror(self , pRoot: TreeNode) -> TreeNode:
        # 空树返回
        if not pRoot: 
            return None
        # 先递归子树
        left = self.Mirror(pRoot.left) 
        right = self.Mirror(pRoot.right)
        # 交换
        pRoot.left = right 
        pRoot.right = left
        return pRoot

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n为二叉树的节点数,访问二叉树所有节点各一次
  • 空间复杂度: O ( n ) O(n) O(n),最坏情况下,二叉树退化为链表,递归栈最大值为 n n n
方法二:栈(扩展思路)

知识点:栈

栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

思路:

二叉树中能够用递归的,我们大多也可以用栈来实现。栈的访问是一种自顶向下的访问,因此我们需要在左右子节点入栈后直接交换,然后再访问后续栈中内容。

具体做法:

  • step 1:优先检查空树的情况。
  • step 2:使用栈辅助遍历二叉树,根节点先进栈。
  • step 3:遍历过程中每次弹出栈中一个元素,然后该节点左右节点分别入栈。
  • step 4:同时我们交换入栈两个子节点的值,因为子节点已经入栈了再交换,就不怕后续没有交换。

图示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java实现代码:

import java.util.*;
public class Solution {
    public TreeNode Mirror (TreeNode pRoot) {
        //空树
        if(pRoot == null)  
            return null;
        //辅助栈
        Stack<TreeNode> s = new Stack<TreeNode>(); 
        //根节点先进栈
        s.push(pRoot); 
        while (!s.isEmpty()){ 
            TreeNode node = s.pop();
            //左右节点入栈
            if(node.left != null) 
                s.push(node.left);
            if(node.right != null) 
                s.push(node.right);
            //交换左右
            TreeNode temp = node.left; 
            node.left = node.right;
            node.right = temp;
        }
        return pRoot;
    }
}

C++实现代码:

class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        //空树
        if(pRoot == NULL)  
            return NULL;
        //辅助栈
        stack<TreeNode*> s; 
        //根节点先进栈
        s.push(pRoot); 
        while (!s.empty()){
            TreeNode* node = s.top();
            s.pop();
            //左右节点入栈
            if (node->left != NULL) s.push(node->left); 
            if (node->right != NULL) s.push(node->right);
            //交换左右 
            TreeNode* temp = node->left;  
            node->left = node->right;
            node->right = temp;
        }
        return pRoot;
    }
};

Python实现代码

class Solution:
    def Mirror(self , pRoot: TreeNode) -> TreeNode:
        # 空树
        if not pRoot:  
            return None
        # 辅助栈
        s = [] 
        # 根节点先进栈
        s.append(pRoot) 
        while s:
            node = s[-1]
            s.pop()
            if node.left:
                # 左右节点入栈
                s.append(node.left) 
            if node.right:
                s.append(node.right)
            # 交换左右
            temp = node.left  
            node.left = node.right
            node.right = temp
        return pRoot

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n为二叉树的节点数,访问二叉树所有节点各一次
  • 空间复杂度: O ( n ) O(n) O(n),最坏情况下,二叉树退化为链表,栈的最大空间为 n n n

题目链接

题目的主要信息:
  • 给定一棵二叉树的根节点,求这棵树的最大深度
  • 深度是指树的根节点到任一叶子节点路径上节点的数量
  • 最大深度是所有叶子节点的深度的最大值
  • 叶子节点是指没有子节点的节点
举一反三:

学习完本题的思路你可以解决如下题目:

BM29. 二叉树中和为某一值的路径(一)

BM31. 对称的二叉树

BM32. 合并二叉树

BM33. 二叉树的镜像

BM36. 判断是不是平衡二叉树

BM39. 序列化二叉树

BM41. 输出二叉树的右视图

方法一:递归(推荐使用)

知识点:二叉树递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。

思路:

最大深度是所有叶子节点的深度的最大值,深度是指树的根节点到任一叶子节点路径上节点的数量,因此从根节点每次往下一层深度就会加1。因此二叉树的深度就等于根节点这个1层加上左子树和右子树深度的最大值,即 r o o t d e p t h = m a x ( l e f t d e p t h , r i g h t d e p t h ) + 1 root_{depth} = max(left_{depth}, right_{depth})+1 rootdepth=max(leftdepth,rightdepth)+1。而每个子树我们都可以看成一个根节点,继续用上述方法求的深度,于是我们可以对这个问题划为子问题,利用递归来解决:

  • 终止条件: 当进入叶子节点后,再进入子节点,即为空,没有深度可言,返回0.
  • 返回值: 每一级按照上述公式,返回两边子树深度的最大值加上本级的深度,即加1.
  • 本级任务: 每一级的任务就是进入左右子树,求左右子树的深度。

具体做法:

  • step 1:对于每个节点,若是不为空才能累计一次深度,若是为空,返回深度为0.
  • step 2:递归分别计算左子树与右子树的深度。
  • step 3:当前深度为两个子树深度较大值再加1。

图示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java实现代码:

import java.util.*;
public class Solution {
    public int maxDepth (TreeNode root) {
        //空节点没有深度
        if(root == null) 
            return 0;
        //返回子树深度+1
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; 
    }
}

C++实现代码:

class Solution {
public:
    int maxDepth(TreeNode* root) {
        //空节点没有深度
        if(root == NULL) 
            return 0;
        //返回子树深度+1
        return max(maxDepth(root->left), maxDepth(root->right)) + 1; 
    }
};

Python实现代码

class Solution:
    def maxDepth(self , root: TreeNode) -> int:
        # 空节点没有深度
        if not root: 
            return 0
        # 返回子树深度+1
        return max([self.maxDepth(root.left), self.maxDepth(root.right)]) + 1

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n为二叉树的节点数,遍历整棵二叉树
  • 空间复杂度: O ( n ) O(n) O(n),最坏情况下,二叉树化为链表,递归栈深度最大为 n n n
方法二:层次遍历(扩展思路)

知识点:队列

队列是一种仅支持在表尾进行插入操作、在表头进行删除操作的线性表,插入端称为队尾,删除端称为队首,因整体类似排队的队伍而得名。它满足先进先出的性质,元素入队即将新元素加在队列的尾,元素出队即将队首元素取出,它后一个作为新的队首。

思路:

既然是统计二叉树的最大深度,除了根据路径到达从根节点到达最远的叶子节点以外,我们还可以分层统计。对于一棵二叉树而言,必然是一层一层的,那一层就是一个深度,有的层可能会很多节点,有的层如根节点或者最远的叶子节点,只有一个节点,但是不管多少个节点,它们都是一层。因此我们可以使用层次遍历,二叉树的层次遍历就是从上到下按层遍历,每层从左到右,我们只要每层统计层数即是深度。

具体做法:

  • step 1:既然是层次遍历,我们遍历完一层要怎么进入下一层,可以用队列记录这一层中节点的子节点。队列类似栈,只不过是一个先进先出的数据结构,可以理解为我们平时的食堂打饭的排队。因为每层都是按照从左到右开始访问的,那自然记录的子节点也是从左到右,那我们从队列出来的时候也是从左到右,完美契合。
  • step 2:在刚刚进入某一层的时候,队列中的元素个数就是当前层的节点数。比如第一层,根节点先入队,队列中只有一个节点,对应第一层只有一个节点,第一层访问结束后,它的子节点刚好都加入了队列,此时队列中的元素个数就是下一层的节点数。因此遍历的时候,每层开始统计该层个数,然后遍历相应节点数,精准进入下一层。
  • step 3:遍历完一层就可以节点深度就可以加1,直到遍历结束,即可得到最大深度。

图示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java实现代码:

import java.util.*;
public class Solution {
    public int maxDepth (TreeNode root) {
        //空节点没有深度
        if(root == null) 
            return 0;
        //队列维护层次后续节点
        Queue<TreeNode> q = new LinkedList<TreeNode>(); 
        //根入队
        q.offer(root); 
        //记录深度
        int res = 0; 
        //层次遍历
        while(!q.isEmpty()){ 
            //记录当前层有多少节点
            int n = q.size(); 
            //遍历完这一层,再进入下一层
            for(int i = 0; i < n; i++){ 
                TreeNode node = q.poll();
                //添加下一层的左右节点
                if(node.left != null) 
                    q.offer(node.left);
                if(node.right != null)
                    q.offer(node.right);
            }
            //深度加1
            res++; 
        }
        return res; 
    }
}

C++实现代码:

class Solution {
public:
    int maxDepth(TreeNode* root) {
        //空节点没有深度
        if(root == NULL) 
            return 0;
        //队列维护层次后续节点
        queue<TreeNode*> q; 
        //根入队
        q.push(root); 
        //记录深度
        int res = 0; 
        //层次遍历
        while(!q.empty()){ 
            //记录当前层有多少节点
            int n = q.size(); 
            //遍历完这一层,再进入下一层
            for(int i = 0; i < n; i++){ 
                TreeNode* node = q.front();
                q.pop();
                //添加下一层的左右节点
                if(node->left) 
                    q.push(node->left);
                if(node->right)
                    q.push(node->right);
            }
            //深度加1
            res++; 
        }
        return res;
    }
};

Python实现代码

import queue
class Solution:
    def maxDepth(self , root: TreeNode) -> int:
        # 空节点没有深度
        if not root: 
            return 0
        # 队列维护层次后续节点
        q= queue.Queue() 
        # 根入队
        q.put(root) 
        # 记录深度
        res = 0
        # 层次遍历
        while not q.empty(): 
            # 记录当前层有多少节点
            n = q.qsize() 
            # 遍历完这一层,再进入下一层
            for i in range(n): 
                node = q.get()
                # 添加下一层的左右节点
                if node.left: 
                    q.put(node.left)
                if node.right:
                    q.put(node.right)
            # 深度加1
            res += 1 
        return res

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n为二叉树的节点数,遍历整棵二叉树
  • 空间复杂度: O ( n ) O(n) O(n),辅助队列的空间最坏为 n n n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值