判断是否是子树 和 判断二叉树是否平衡

一、判断是否是子树

有两棵很大的二叉树:T1有上百万个结点,T2有上百个结点。写程序判断T2是否为T1的子树。

思路:先在T1中找到T2的根结点,然后依次去匹配它们的左右子树即可。

注意:T1中的结点可能包含多个与T2根结点的值相同的结点。因此, 在T1中查找T2的根结点时,如果找到与T2匹配的子树,则返回真值;否则,还要继续查找, 直到在T1中找到一棵匹配的子树或是T1中的结点都查找完毕。

代码如下:

bool match(Node* r1, Node* r2){                           //匹配左右子树
    if(r1 == NULL && r2 == NULL) return true;
    else if(r1 == NULL || r2 == NULL) return false;
    else if(r1->key != r2->key) return false;
    else return match(r1->lchild, r2->lchild) && match(r1->rchild, r2->rchild);
}
bool subtree(Node* r1, Node* r2){                        //用于寻找T1中和T2的根相同的节点,找到了,匹配左右子树,没找到,分别向T1的左右子树寻找
    if(r1 == NULL) return false;                         //说明没有在T1中找到想要的根节点
    else if(r1->key == r2->key){
        if(match(r1, r2)) return true;
        else return subtree(r1->lchild, r2) || subtree(r1->rchild, r2);  
    }
    else return subtree(r1->lchild, r2) || subtree(r1->rchild, r2);
}

bool contain_tree(Node* r1, Node* r2){
    if(r2 == NULL) return true;
    else return subtree(r1, r2);
}

二、判断二叉树是否平衡

题目:输入一棵二叉树的根结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的高度相差不超过1,那么它就是一棵平衡二叉树。例如下图中的二叉树就是一棵平衡二叉树:

《判断是否是子树 和 判断二叉树是否平衡》

有了求二叉树的高度的经验之后再解决这个问题,我们很容易就能想到一个思路:在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的高度。如果每个结点的左右子树的高度相差都不超过1,按照定义它就是一棵平衡的二叉树。这种思路对应的代码如下:

bool IsBalanced(BinaryTreeNode* pRoot)
{
    if(pRoot == NULL)
        return true;
 
    int left = TreeDepth(pRoot->m_pLeft);
    int right = TreeDepth(pRoot->m_pRight);
    int diff = left - right;
    if(diff > 1 || diff < -1)
        return false;
 
    return IsBalanced(pRoot->m_pLeft) && IsBalanced(pRoot->m_pRight);
}

上面的代码固然简洁,但我们也要注意到由于一个节点会被重复遍历多次,这种思路的时间效率不高。例如在函数IsBalance中输入上图中的二叉树,首先判断根结点(值为1的结点)的左右子树是不是平衡结点。此时我们将往函数TreeDepth输入左子树根结点(值为2的结点),需要遍历结点4、5、7。接下来判断以值为2的结点为根结点的子树是不是平衡树的时候,仍然会遍历结点4、5、7。毫无疑问,重复遍历同一个结点会影响性能。接下来我们寻找不需要重复遍历的算法。

如果我们用后序遍历的方式遍历二叉树的每一个结点,在遍历到一个结点之前我们已经遍历了它的左右子树。只要在遍历每个结点的时候记录它的高度(某一结点的高度等于它到叶节点的路径的长度),我们就可以一边遍历一边判断每个结点是不是平衡的。


关于递归返回值的说明:return true;

一者是检查输入的借口,二者表示叶子结点一定是平衡的,故可返回true(递归出口)! 但这并不意味着整棵树都是平衡的(比如这句代码只使得递归返回到叶子结点的上一层),只有所有的子树全部是平衡都返回了true(实际只有当true时才能返回到上一层,要不然就直接退出递归了!!即 if(IsBalanced(pRoot->m_pLeft, &left) && IsBalanced(pRoot->m_pRight, &right))  的功能 )  到上一层,并使得最终的整棵树返回true(这时就正常退出了整个递归)到调用函数中,才能判定整棵树平衡。

下面是这种思路的参考代码:

bool IsBalanced(BinaryTreeNode* pRoot, int* pDepth)
{
    if(pRoot == NULL)
    {
        *pDepth = 0;      //我觉得应该是 -1 
        return true;      //这一句必须加,一者是检查输入的借口,二者加上之后,表示叶子结点一定是平衡的,故可返回true!但这并不意味着整棵树都是平衡的(比如这句代码只使得递归返回到叶子结点的上一层),只有所有的子树全部是平衡都返回了true(实际只有当true时才能返回到上一层,要不然就直接退出递归了!!)到上一层,并使得最终的整棵树返回true(这时就正常退出了整个递归)到调用函数中,才能判定整棵树平衡。
    }
 
    int left, right;
    if(IsBalanced(pRoot->m_pLeft, &left)
        && IsBalanced(pRoot->m_pRight, &right))  //注意是if语句(因为每一个递归都是有返回值的),子树都平衡才能继续向树顶归溯。
    {
        int diff = left - right;
        if(diff <= 1 && diff >= -1)       //上一个if是判断上一层,这一个 if 就是处理这一层
        {
            *pDepth = 1 + (left > right ? left : right);
            return true;   //这一句起到返回上一层递归的作用,使得回溯能够继续(直到退出整个递归为止)。
        }
    }
 
    return false;     
}

我们只需要给上面的函数传入二叉树的根结点以及一个表示结点高度的整型变量就可以了:

bool IsBalanced(BinaryTreeNode* pRoot)
{
    int depth;
    return IsBalanced(pRoot, &depth);
}

在上面的代码中,我们用后序遍历的方式遍历整棵二叉树。在遍历某结点的左右子结点之后,我们可以根据它的左右子结点的高度判断它是不是平衡的,并得到当前结点的高度。当最后遍历到树的根结点的时候,也就判断了整棵二叉树是不是平衡二叉树了。

注:原文中搞混了深度与高度的概念,应该是高度,已修正。 点击打开链接

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/langqing12345/article/details/43206867
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞