数据结构和算法十三

剑指 Offer 26. 树的子结构

题目:输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
           给定的树 A:
                   3
                  / \
               4 5
               / \
             1  2
          给定的树 B:
                  4
                 /
               1
          返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:
          输入:A = [1,2,3], B = [3,1]
          输出:false
示例 2:
          输入:A = [3,4,5,1,2], B = [4,1]
          输出:true
限制:
          0 <= 节点个数 <= 10000

/*解题思路:
若树B是树A的子结构,则子结构的根节点可能为树A的任意一个节点。因此,判断树B是否是树A的子结构,需完成以下两步工作:
先序遍历树A中的每个节点nA;(对应函数 isSubStructure(A, B))
判断树A中以nA为根节点的子树是否包含树B。(对应函数 recur(A, B))
算法流程:
名词规定:树A的根节点记作节点A,树B的根节点称为节点B。
recur(A, B) 函数:
终止条件:
	当节点B为空:说明树B已匹配完成(越过叶子节点),因此返回true;
	当节点A为空:说明已经越过树A叶子节点,即匹配失败,返回false;
	当节点A和B的值不同:说明匹配失败,返回false;
返回值:
	判断A和B的左子节点是否相等,即 recur(A.left, B.left) ;
	判断A和B的右子节点是否相等,即 recur(A.right, B.right) ;
isSubStructure(A, B) 函数:
特例处理:当树A为空或树B为空时,直接返回false ;
返回值:若树B是树A的子结构,则必满足以下三种情况之一,因此用或 || 连接;
	以节点A为根节点的子树包含树B,对应 recur(A, B);
	树B是树A左子树的子结构,对应 isSubStructure(A.left, B);
	树B是树A右子树的子结构,对应 isSubStructure(A.right, B);
	以上 2. 3. 实质上是在对树A做先序遍历 。
复杂度分析:
	时间复杂度O(MN):其中M,N分别为树A和树B的节点数量;先序遍历树A占用O(M),每次调用recur(A, B)判断占用 O(N)。
	空间复杂度O(M):当树A和树B都退化为链表时,递归调用深度最大。当M≤N时,遍历树A与递归判断的总递归深度为M;当 M>N时,最差情况为遍历至树A叶子节点,此时总递归深度为M。
*/

在这里插入图片描述

class Method{
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        return(A!=null&&B!=null)&&(recur(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B));
    }
    boolean recur(TreeNode A, TreeNode B) {
        if(B == null) return true;
        if(A == null || A.val != B.val) return false;
        return recur(A.left, B.left) && recur(A.right, B.right);
    }
}

剑指 Offer 27. 二叉树的镜像

题目:请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
                    4
                  /   \
                 2    7
                / \    / \
              1  3  6  9
镜像输出:
                    4
                  /   \
                 7    2
                / \    / \
              9   6 3  1
示例 1:
          输入:root = [4,2,7,1,3,6,9]
          输出:[4,7,2,9,6,3,1]
限制:
          0 <= 节点个数 <= 1000

二叉树镜像定义: 对于二叉树中任意节点root ,设其左 / 右子节点分别为 left,right ;则在二叉树的镜像中的对应 root 节点,其左 / 右子节点分别为 right,left 。
在这里插入图片描述

方法一:递归法

/*
根据二叉树镜像的定义,考虑递归遍历(dfs)二叉树,交换每个节点的左 / 右子节点,即可生成二叉树的镜像。
递归解析:
终止条件:当节点root为空时(即越过叶节点),则返回null;
递推工作:
	初始化节点tmp,用于暂存root的左子节点;
	开启递归右子节点mirrorTree(root.right),并将返回值作为root的左子节点。
	开启递归左子节点mirrorTree(tmp),并将返回值作为root的右子节点 。
返回值:返回当前节点root;
Q:为何需要暂存root 的左子节点?
A:在递归右子节点 “root.left = mirrorTree(root.right);” 执行完毕后,root.leftroot.left的值已经发生改变,此时递归左子节点 mirrorTree(root.left)则会出问题。
复杂度分析:
时间复杂度O(N):其中N为二叉树的节点数量,建立二叉树镜像需要遍历树的所有节点,占用O(N)时间。
空间复杂度O(N):最差情况下(当二叉树退化为链表),递归时系统需使用O(N)大小的栈空间。
*/
class Method1{
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;
        TreeNode tmp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(tmp);
        return root;
    }
}

方法二:辅助栈(或队列)

/*
利用栈(或队列)遍历树的所有节点node,并交换每个node的左/右子节点。
算法流程:
特例处理:当root为空时,直接返回null ;
初始化:栈(或队列),本文用栈,并加入根节点root 。
循环交换:当栈stack为空时跳出;
出栈:记为node ;
添加子节点:将node左和右子节点入栈;
交换:交换node 的左 / 右子节点。
返回值:返回根节点root 。
复杂度分析:
时间复杂度O(N):其中N为二叉树的节点数量,建立二叉树镜像需要遍历树的所有节点,占用O(N)时间。
空间复杂度O(N):如下图所示,最差情况下,栈stack最多同时存储2N+1/2个节点,占用O(N)额外空间。
*/

在这里插入图片描述

class Method2{
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;
        Stack<TreeNode> stack = new Stack<>() {{ add(root); }};
        while(!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if(node.left != null) stack.add(node.left);
            if(node.right != null) stack.add(node.right);
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        return root;
    }
}

总结:

       不管是做什么事情都应该静下心来!戒躁戒傲,慢慢的才会发现自己的缺点!然后通过不停的鞭策自己使自身进步!然后更上一层楼!都是一个积累的过程!这个过程可能很艰辛!但是结果却是美好的!
       一旦选择好方向就一定要坚持下去!不要轻易言弃!如果觉得累了,可以暂且的停下来思考一番,或许你会找到一个新的方向和新的答案!就像我坚持每天写一篇博客一样!我也不知道会有多少小伙伴去看,但是我依然每天都坚持写!有没有小伙伴看是一回事?写不写又是另外一回事了?渐渐的我的博客多了起来!它也成为了我生活中的一部分!每天更新一篇,如果有事耽误了,我会在后一天及时补上!我只是希望我的博客能给更多的小伙伴带来帮助!渐渐的也担起了一份责任和担当!未来我会一直坚持下去的!
       最后,愿我们都能在各行各业中能够取得不同的成就!能够用自身的所学知识为国家贡献出自己的一份力量!一起加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值