(7/31)剑指 Offer: 26&27&28

1.剑指offer:26题.树的子结构
力扣icon-default.png?t=MBR7https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof/

方法:递归

        首先,通过遍历A树寻找B树的根结点rootB,如果存在则继续遍历,否则返回false。

        再是,在A树中找到B树的rootB后,遍历AB两树的左右孩子是否相同,

        然后,当出现不同时,在A树中rootB位置的基础上,遍历寻找下一个rootB,重复上步。

        最后,如果在A树中找到B树,返回true,否则返回false。

算法思路

        1. 构造辅助函数isSub,遍历AB树,及其左右孩子,同时对二者结点进行比较。

                1> 当B树为空

                        表示已遍历完B树,且已遍历的AB树结点均一致,说明B是A的子树。返回true。

                2> 当A树为空

                        表示已遍历完A树,但B树还有结点未遍历,说明B不是A的子树。返回false。

                3> 当AB树对应结点不一致

                        说明当前的A树不包含B树,返回false。

                注:代码中将2,3结合。且1,2,3均为递归的限制条件

                4> 当前的AB树对应结点一致(递归)

                        既然当前的结点一致,那么要继续遍历并判断AB树该结点的左右孩子是否一致。

        2. 在isSubStructure函数中,

                1> 据题意,首先明确当A,B任一树为空树时,返回false。 

                2> 调用函数isSub遍历AB树,判断A树中是否含B树。

                3> 再调用函数本身isSubStructure,判断A的左子树和右子树中是否含B树。

                4> 最后,当2,3中任一函数返回true,那么就代表B树是A树的子树,返回true。

算法图

复杂度分析

        假设A树结点有M个,B树结点有N个。

        时间复杂度:O(M*N)。

                递归算法的时间复杂度 = 递归的次数 * 每次递归的时间复杂度。

                本题的递归次数取决于A树结点数M,每次递归的时间复杂度取决于B树结点数N。

        空间复杂度:O(M)。

                递归算法的空间复杂度 = 递归的深度 * 每次递归的空间复杂度。

                当AB树退化为链表时,递归深度最深。

                当M<=N时,遍历A树和判断递归的深度为M。

                当M>N时,最坏情况即B树不是A树的子树时,需要遍历A树的深度为M。

代码

    // isSub函数的作用: 遍历AB树,并判断A树中是否含B树
    bool isSub(TreeNode* A, TreeNode* B){

        // B树为空,表示已经遍历完B树,且与A树结点均一致,所以B树是A树的子集
        if(B == NULL)
            return true;

        // A树为空,表示已经遍历完A树,但B树还未遍历完,所以B树不是A树的子集
        // 当A,B树的结点不同时,表示A不包含B
        if(A == NULL || A->val != B->val)
            return false;

        // 递归循环遍历A的左孩子和B的左孩子、A的右孩子和B的右孩子
        // 只有对应结点都相等时,才能判断B是A的子树
        return isSub(A->left,B->left) && isSub(A->right,B->right);
    }

    bool isSubStructure(TreeNode* A, TreeNode* B) {

        // 题目约定空树不是任一树的子树
        if(A == NULL || B == NULL)    
            return false;

        // 判断A树中是否含B树,并循环遍历A树的左右孩子是否含B树
        return isSub(A,B) || isSubStructure(A->left,B) || isSubStructure(A->right,B);
    }

2.剑指offer:27题.二叉树的镜像

力扣icon-default.png?t=MBR7https://leetcode.cn/problems/er-cha-shu-de-jing-xiang-lcof/

方法:递归法

        遍历树的每一层,

        第一层只有一个结点不需要镜像,

        从第二层开始(两个结点)互换两结点顺序,每个结点又交换自己的左右孩子,依次类推。

算法思路

        1. 已知需要不停交换两结点位置,故构造辅助函数exchange_node,完成结点的交换。

        2. 将mirrorTree函数写成一个递归函数。

                1> 限制条件

                        当本树为空树时,没有结点可镜像,故返回NULL。

                2> 递归方程

                        交换当前结点的左右孩子。

                3> 递推方程

                        递归当前结点的左右孩子,方便进一步交换其左右孩子的左右孩子。

算法图

复杂度分析

        假设树结点有N个。

        时间复杂度:O(N)。

                递归算法的时间复杂度 = 递归的次数 * 每次递归的时间复杂度。

                本题的递归次数取决于树结点数,每次递归的时间复杂为O(1)。

                当树退化为链表时,递归次数最多,为结点数N。

        空间复杂度:O(N)。

                递归算法的空间复杂度 = 递归的深度 * 每次递归的空间复杂度。

                当树退化为链表时,递归深度最深,为结点个数N,每次的空间复杂度为O(1)。

代码

    // 辅助函数:用于交换结点左右孩子
    void exchange_node(TreeNode* root){
        TreeNode* node = root->left;
        root->left = root->right;
        root->right = node;
    }
    TreeNode* mirrorTree(TreeNode* root) {
        if(root == NULL)
            return NULL;
        exchange_node(root);        // 交换root结点的左右孩子
        mirrorTree(root->left);     // 交换root左结点的左右孩子
        mirrorTree(root->right);    // 交换root右结点的左右孩子
        return root;
    }

3.剑指offer:28题.对称的二叉树

力扣icon-default.png?t=MBR7https://leetcode.cn/problems/dui-cheng-de-er-cha-shu-lcof/

方法:递归法

        根据对称性,把树分为外部和内部。

        当外部的结点和内部的结点都对应相等时,说明该树具有对称性。

        需要一个辅助函数,用来比较对称的结点是否相等。

        当树为空树时,返回true。

        当树不为空树时,利用辅助函数比较每一层的外部结点和内部节点是否相等,相等返回true。

算法思路

        1. 构造函数isCompare,比较结点的左右子树是否相等。

                1> 比较结点的左右子树 (递归的限制条件&递归方程)

                        i. 左右子树为空

                                说明它们的父节点为叶子结点,而已比较的结点都对称,所以返回true。

                        ii. 只有其中一个子树为空

                                说明该二叉树不可能对称,所以返回false。

                        iii. 左右子树都存在     

                                相同时表示对称,返回true,反之,返回false。

                        注:i,ii为限制条件,iii为递归方程。

                2> 确定外部和内部,比较树的对称位置结点是否相同(递推方程)

                        外部=左孩子的左孩子&右孩子的右孩子

                        内部=左孩子的右孩子&右孩子的左孩子

                3> 只有当外部和内部都对应相等时,整棵树才对称,返回true。

        2. 在函数isSymmetric中,        

                当树为空树时,返回true。

                不为空,调用函数isCompare比较结点左右子树是否有对称性。

算法图

复杂度分析

        假设树结点有N个。

        时间复杂度:O(N)。

                递归算法的时间复杂度 = 递归的次数 * 每次递归的时间复杂度。

                每一次递归都递归两个结点,递归次数为N/2,所以时间复杂度为O(N)。

        空间复杂度:O(N)。

                递归算法的空间复杂度 = 递归的深度 * 每次递归的空间复杂度。

                当树退化为链表时,递归深度为结点个数N,

                每次递归左右子树,空间复杂度为O(2),是常数阶可记为O(1),

                所以空间复杂度为O(N)。

代码

    // 比较节点的左右孩子是否对称
    bool isCompare(TreeNode* left, TreeNode* right){
        if(left == NULL && right == NULL)    // 表示只有一个结点,不用比较孩子,返回true
            return true;

        // 表示节点左右孩子不对称
        else if(left != NULL && right == NULL)     
            return false;
        else if(left == NULL && right != NULL)    
            return false;
        if(left->val != right->val)        
            return false;

        // 确定外部和内部
        bool outside = isCompare(left->left,right->right);
        bool inside = isCompare(right->left,left->right);

        // 只有当外部和内部对应结点都相同时,才说明树有对称性。
        return outside && inside;
    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL)    // 空树,返回true
            return true;
        return isCompare(root->left,root->right);    // 递归比较根节点的左右子树是否对称
    }

23. 1.6       第七次

        今天三题都用递归解决,在此,对递归的写法做个小结:

递归三部曲:

1. 确定限制条件

        递归到递无可递的时候,就让递归调用的栈全部弹出。

        通常都是结点为空,递归到0等情况,不再递归,而是返回,标志调用完毕,可弹出。

2. 写出递归方程

        递归方程就是每次递归要做的事情,所以递归适用于需要重复执行某一块代码的情况。

3. 明确递推方程

        让栈朝着我们需要的方向进行调用,只有遇到限制条件时,结束递归,否则持续调用。

闲谈:

        制图依旧需要大量的时间,但我已经有点熟能生巧了,速度越来越快(但其实还是很慢)。

        所以今天比以往写博客的速度要更快一点,很开心,能感受到进步啦。

        过去,在要写递归的时候,我总是害怕,担忧,认为自己肯定写不对,

        但今天我顺着自己的思路,用递归写出一道题了,首次即通过,

        虽然题目确实不难,但对我来说,是一个不小的肯定!

        再接再厉,继续加油!

        最后,本文如有问题,欢迎斧正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值