树的子结构

题目描述

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

第一想法

  • 如何遍历二叉树?

前中后序遍历

  • 如何判断子结构?
  • 前序遍历结果相等就可以证明子结构了吗?(需不需要两种遍历同时符合,因为之前一题还原需要两种遍历序列)
  • 比较子串我想到了KMP算法(字符子串和数字子串一样吗)

需要两种遍历结果

XXX 缺乏树遍历的知识

代码如下,还是很长,且无法通过
思路是返回AB两树的前序和中序遍历序列,各自有从属关系时则是子结构.
后来仔细想了想,虽然前序+中序能够确定一个二叉树,但是不能确定子结构的从属关系,如下:


/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        vector<int> tree1pre, tree1in, tree2pre, tree2in;
        inTree(pRoot1, tree1in);
        preTree(pRoot1, tree1pre);
        inTree(pRoot2, tree2in);
        preTree(pRoot2, tree2pre);
        int k,m = 0;
        bool flag1 = false;
        bool flag2 = false;
        for(int i = 0; i < tree1pre.size(); i++){
            if(tree1pre[i] == tree2pre[0]){
                k = i;
                for(int j = 0; j < tree2pre.size(); j++){
                    if(tree1pre[k] == tree2pre[j]){
                        m++;
                        k++;
                        if(m == tree2pre.size())
                            flag1 = true;
                    }
                    else{
                        m = 0;
                        break;
                    }
                }
            }
        }
        m = 0;
        for(int i = 0; i < tree1in.size(); i++){
            if(tree1in[i] == tree2in[0]){
                k = i;
                for(int j = 0; j < tree2in.size(); j++){
                    if(tree1in[k] == tree2in[j]){
                        m++;
                        k++;
                        if(m == tree2in.size())
                            flag2 = true;
                    }
                    else{
                        m = 0;
                        break;
                    }
                }
            }
        }
        return flag1 && flag2;
    }
    
    void inTree(TreeNode* pRoot, vector<int>& tree){
        if(pRoot == NULL)    return;
        else{
            inTree(pRoot -> left, tree);
            tree.push_back(pRoot -> val);
            inTree(pRoot -> right, tree);
        }
    }
    
    void preTree(TreeNode* pRoot, vector<int>& tree){
        if(pRoot == NULL)    return;
        else{
            tree.push_back(pRoot -> val);
            preTree(pRoot -> left, tree);
            preTree(pRoot -> right, tree);
        }
    }
};

这一版的程序在我自己的Vscode上通过测试.完成依据两种遍历数组的从属关系返回True或False,对树2长度>树1的情况还没做处理,我觉得是基本完成了.但没有AC,两个循环O(n^2)时间复杂度也比较高了

  • 原本也想过直接对树进行比较
  • 原本也想过用递归实现

?为什么拒绝直接对树进行比较的方法
因为想着从root向下,遇到的第一个结点val相等,万一不是,还要if讨论,再往下遇到第二个结点与子结构的相等,又多出一个分支,加之没有之前对树进行比较的经验,让我把它化为vector再处理.

?为什么放弃递归实现
因为想着传递上来的应该是一个值而非布尔变量,想着可能无法用递归实现.

不是很能理解递归是因为思路总是由顶向下而非递归的由底往上

我再用递归试试.
如果使用if pRoot == NULL --- return false作为递归基,那么就不会有return true的情况


看了讨论我发现问题是我本想以一个函数的递归来解决任务.结果其实是分为找到开始遍历的根节点看子树是否一致这两个函数来做的.原来的代码 ↓

    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL)    return false;
        if(pRoot2 == NULL)    return true;
        else{
            if(pRoot1 -> val == pRoot2 -> val)
                return HasSubtree(pRoot1 -> left, pRoot2 -> left) && HasSubtree(pRoot1 -> right, pRoot2 -> right);
            else{
                return HasSubtree(pRoot1 -> left, pRoot2) || HasSubtree(pRoot1 -> right, pRoot2);
            }
        }
    }

AC的代码

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL || pRoot2 == NULL )    return false;
/*以下四行改成第二行一行也可以通过*/
        if(pRoot1 -> val == pRoot2 -> val)
            return IsSubTree(pRoot1, pRoot2) || HasSubtree(pRoot1 -> left, pRoot2) || HasSubtree(pRoot1 -> right, pRoot2);
        else{
            return HasSubtree(pRoot1 -> left, pRoot2) || HasSubtree(pRoot1 -> right, pRoot2);
        }
    }
private:
    bool IsSubTree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2 == NULL)//需要注意的是这个要写在前面,因为当两树原本一致都遍历到底部,同为NULL,应该返回true.
            return true;
        if(pRoot1 == NULL)
            return false;
        if(pRoot1 -> val == pRoot2 -> val)
            return IsSubTree(pRoot1 -> left, pRoot2 -> left) && IsSubTree(pRoot1 -> right, pRoot2 -> right);
        else{
            return false;
        }
    }
};

可是为什么要把他们分开来呢?不分开我又重新改了第一遍递归的代码

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2 == NULL)    return true;
        if(pRoot1 == NULL)    return false;
        
        else{
            if(pRoot1 -> val == pRoot2 -> val)
                return HasSubtree(pRoot1 -> left, pRoot2 -> left) && HasSubtree(pRoot1 -> right, pRoot2 -> right) || HasSubtree(pRoot1 -> left, pRoot2) || HasSubtree(pRoot1 -> right, pRoot2);
            else{
                return HasSubtree(pRoot1 -> left, pRoot2) || HasSubtree(pRoot1 -> right, pRoot2);
            }
        }
    }
};

通过率是70%
问题出在哪了呢?

用例:
{8,#,9,3,2},{}
对应输出应该为:
false
你的输出为:
true

本函数的递归无法辨别proot2是到底了还是根本本来就是NULL
所以将遍历到值相等与比较分开.

最简洁的代码

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1 == NULL || pRoot2 == NULL )    return false;
        return IsSubTree(pRoot1, pRoot2) || HasSubtree(pRoot1 -> left, pRoot2) || HasSubtree(pRoot1 -> right, pRoot2);
    }
private:
    bool IsSubTree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2 == NULL)    return true;
        if(pRoot1 == NULL)    return false;
        return pRoot1 -> val == pRoot2 -> val && IsSubTree(pRoot1 -> left, pRoot2 -> left) && IsSubTree(pRoot1 -> right, pRoot2 -> right);
    }
};

本题主要的坑是一个函数的递归基不满足条件.

问题:NULL null nullptr分别用在哪里?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 解题思路 总...
    Mereder阅读 1,495评论 0 0
  • 题目描述: 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 分析: 首...
    夏臻Rock阅读 2,454评论 0 0
  • 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 分析 首先设...
    BluthLeee阅读 952评论 0 0
  • 题目 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 程序核心思想 这...
    Maxinxx阅读 1,625评论 0 1
  • 感悟 第五章的后半章,还是在讲心理表征在刻意练习中的重要地位。作者反反复复地在强调这个概念。 “it is muc...
    初一Susie阅读 1,576评论 0 0

友情链接更多精彩内容