判断两个二叉树的结构与结点值是否相同——递归实现

递归实现判断两个二叉树的结构与结点值是否相等

整理思路

因为整个算法是要实现比较二叉树的结构与结点值,那么就有四种情况会产生:1.两个二叉树完全相同;2.两个二叉树只有结构不同;3.两个二叉树只有结点值不同;4.两个二叉树结构与结点值都不同。具体4种情况如下图所示:
情况1:两个二叉树相同
情况1:两个二叉树相同
情况2:二叉树结构不同
情况2:二叉树结构不同
情况3:只有结点值不同
情况3:只有结点值不同
情况4:结构与结点值都不同
情况4:结构与结点值都不同
因为需要比较每个结点的左右子树,所以这里采用递归的方式进行编写算法。
整体思路为以下三步:
1.创建二叉树;
2.遍历二叉树(用于检查二叉树,可省略)
3.比较两棵二叉树

二叉树创建

首先需要创建二叉树结构体:

typedef struct BiTNode {
    int data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

二叉树的创建类似于递归前序遍历,先创建结点值,接着递归调用结点的左子树与右子树。
因为数据域采用的int类型的值,所以这里规定若输入9999,那么该结点为NULL。

void creatTree (BiTree *T) {
    int tData;  //输入的结点值
    cout<<"please enter the number that you want to insert. 9999 is NULL:"<<endl;
    cin>>tData;
    if (tData == 9999) {
        (*T) = NULL;
    } else {
        (*T) = (BiTree)malloc(sizeof(BiTree));  //为结点分配内存空间
        (*T)->data = tData;
        creatTree(&(*T)->lchild);
        creatTree(&(*T)->rchild);
    }
}

二叉树遍历

遍历则是为了更好地检查自己创建的二叉树,所以这里只采用了递归先序遍历。

void preOrder (BiTree T) {
    if (T != NULL) {
        visit(T);
        preOrder(T->lchild);
        preOrder(T->rchild);
    }
}

相应的visit()方法:

void visit (BiTree T) {
    cout<<T->data<<"  ";
} 

二叉树比较

因为考虑到该算法不仅要比较二叉树的结构,同时还有结点值,那么就使用4个状态值表达两个二叉树的情况:1(二叉树相同);2(结构不同);3(结点值不同);4(结构与结点值都不同)。

二叉树的比较的逻辑为:
1.先判断结点是否都为NULL,若都为NULL,那么这里的结构是没有问题的。也可以说判断这两棵树是否都为空树,只是这里的空树指的是以该结点为起点的树,即该结点与其下面的所有子节点构成的一棵树。如图所示,假设红色箭头是递归到的位置,那么该结点就为一棵空树:
空树的说明
2.接着判断二叉树的结构是否相同,若结构都不同了,那么就没有必要再比较这棵树(或子树)的值了。
3.若二叉树不为空,且结构相同,那么就比较二叉树的结点值。

因为在递归左右子树后需要返回正确的状态码,那么需要考虑以下4种情况:

  1. 左右子树返回值完全相同;
  2. 左右子树中至少有一棵子树结构与结点值都不同;
  3. 左右子树中,一棵子树结构不同,另一棵子树结点值不同;
  4. 左右子树中,一棵子树完全相同,另一棵有问题;

针对以上4种情况处理的方式为:

  1. 返回左右子树任意一棵树的返回值;
  2. 因为某一棵子树已经完全不同了,那么整棵树都是完全不同的,所以返回4;
  3. 因为一棵结构不同,一棵值不同,那么合在一起就是结构也不同,值也不同,所以返回4;
  4. 因为有一棵树有问题,那么整棵树都有问题,而且该问题就是出问题的那棵树的问题。

但是这里不能盲目的书写if语句,因为需要考虑到相应的事件出现的概率,从而减少在if语句中花费的时间,提高程序执行的效率。所以可以作出一个矩阵进行相应的分析,如图所示,前一个数字代表左子树返回的情况,后一个数字代表右子树返回的情况:
在这里插入图片描述
那么从矩阵中可以分析得到:

  1. 两个子树情况相同:一共有4种可能,即11、22、33、44,出现的概率为1/4;
  2. 左右子树中,一棵子树完全相同,另一棵有问题:一共有7种可能,即11、12、13、14、21、31、41,出现的概率为7/16;
  3. 左右子树中至少有一棵子树结构与结点值都不同:一共有7种可能,即14、24、34、44、41、42、43,出现的概率为7/16;
  4. 左右子树中,一棵子树结构不同,另一棵子树结点值不同:一共有2种可能,即23、32,出现的概率为1/8;

因为2与3概率相同,所以无论谁在前后都一样,整体代码如下:

int BiTreeContrast (BiTree T1, BiTree T2) {

    //判断结构: 是否为NULL, 若是, 则结构相同
    if (T1 == NULL && T2 == NULL) {
        return 1;
    } 
    //判断结构: 是否一个为NULL, 另一个不为NULL, 若是, 则结构不同
    //因为上面已经判断过了两个都为NULL的情况, 所以这里无需考虑两个都为NULL的情况
    else if (T1 == NULL || T2 == NULL) {
        return 2;
    } 
    //判断结构: 两个都不为NULL
    else {
        //判断结点值: 若两个相同, 则递归调用左右孩子, 之后返回1
        if (T1->data == T2->data) {
            int leftCompare = BiTreeContrast(T1->lchild, T2->lchild);  //接受递归左子树的值
            int rightCompare = BiTreeContrast(T1->rchild, T2->rchild);  //接受递归右子树的值
            
            //只要有一个子树为4, 那么整棵树都为4
            if (leftCompare == 4 || rightCompare == 4) {
                return 4;
            }
            //一棵子树完全相同, 另外一棵子树结构或结点值不同, 则返回该子树的值
            else if ((leftCompare == 1 && rightCompare != 1) || (leftCompare != 1 && rightCompare == 1)) {
                int max;
                //将大的值赋值给max, 考虑两值相等的情况, 则无需再走else
                if (leftCompare >= rightCompare) {
                    max = leftCompare;
                } else {
                    max = rightCompare;
                }
                return max;
            }
            //左右结点返回的情况都完全相同
            else if (leftCompare == rightCompare) {
                return leftCompare;
            } 
            //左右子树某棵结构不同, 另外一棵结点值不同
            else if ((leftCompare == 2 && rightCompare == 3) || (leftCompare == 3 && rightCompare == 2)) {
                return 4;
            }
        }
        return 3;
    }
}

总代码

总体代码如下:

#include <iostream>
using namespace std;

typedef struct BiTNode {
    int data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

/**
 * 递归构造树:
 *  1.类比先序遍历, 输入结点值: 输入9999则该结点为NULL
 *                            输入其余数值则为该结点值
 *  2.若输入非9999, 则递归访问其左结点, 再递归访问其右结点
 * 
 * param: T (需要创建的树)
 */ 
void creatTree (BiTree *T) {
    int tData;  //输入的结点值
    cout<<"please enter the number that you want to insert. 9999 is NULL:"<<endl;
    cin>>tData;
    if (tData == 9999) {
        (*T) = NULL;
    } else {
        (*T) = (BiTree)malloc(sizeof(BiTree));  //为结点分配内存空间
        (*T)->data = tData;
        creatTree(&(*T)->lchild);
        creatTree(&(*T)->rchild);
    }
}

/**
 * 访问结点值
 * param: T (需要遍历的结点)
 */ 
void visit (BiTree T) {
    cout<<T->data<<"  ";
} 

/**
 * 先序遍历:
 *  1.判断结点值是否为空;
 *  2.若不为空, 则打印结点值, 递归调用左孩子, 递归调用右孩子
 * 
 * param: T (需要遍历的结点)
 */ 
void preOrder (BiTree T) {
    if (T != NULL) {
        visit(T);
        preOrder(T->lchild);
        preOrder(T->rchild);
    }
}

/**
 * 比较两棵二叉树结构与结点值
 * 
 * param: T1 (比较的第一棵树), T2 (比较的第二棵树)
 * return: 1 (完全相同), 2 (结构不同), 3 (结点值不同), 4 (结点值与结构都不同)
 */ 
int BiTreeContrast (BiTree T1, BiTree T2) {

    //判断结构: 是否为NULL, 若是, 则结构相同
    if (T1 == NULL && T2 == NULL) {
        return 1;
    } 
    //判断结构: 是否一个为NULL, 另一个不为NULL, 若是, 则结构不同
    //因为上面已经判断过了两个都为NULL的情况, 所以这里无需考虑两个都为NULL的情况
    else if (T1 == NULL || T2 == NULL) {
        return 2;
    } 
    //判断结构: 两个都不为NULL
    else {
        //判断结点值: 若两个相同, 则递归调用左右孩子, 之后返回1
        if (T1->data == T2->data) {
            int leftCompare = BiTreeContrast(T1->lchild, T2->lchild);  //接受递归左子树的值
            int rightCompare = BiTreeContrast(T1->rchild, T2->rchild);  //接受递归右子树的值
            
            //只要有一个子树为4, 那么整棵树都为4
            if (leftCompare == 4 || rightCompare == 4) {
                return 4;
            }
            //一棵子树完全相同, 另外一棵子树结构或结点值不同, 则返回该子树的值
            else if ((leftCompare == 1 && rightCompare != 1) || (leftCompare != 1 && rightCompare == 1)) {
                int max;
                //将大的值赋值给max, 考虑两值相等的情况, 则无需再走else
                if (leftCompare >= rightCompare) {
                    max = leftCompare;
                } else {
                    max = rightCompare;
                }
                return max;
            }
            //左右结点返回的情况都完全相同
            else if (leftCompare == rightCompare) {
                return leftCompare;
            } 
            //左右子树某棵结构不同, 另外一棵结点值不同
            else if ((leftCompare == 2 && rightCompare == 3) || (leftCompare == 3 && rightCompare == 2)) {
                return 4;
            }
        }
        return 3;
    }
}

int main () {
    BiTree T1, T2;
    cout<<"please construct the first tree:"<<endl;
    creatTree(&T1);
    cout<<"please construct the second tree:"<<endl;
    creatTree(&T2);

    preOrder(T1);
    cout<<endl;
    preOrder(T2);
    cout<<endl;

    switch (BiTreeContrast(T1, T2))
    {
    case 1:
        cout<<"These trees are completely the same!"<<endl;
        break;
    case 2:
        cout<<"These trees have different structures!"<<endl;
        break;
    case 3:
        cout<<"These trees have different data!"<<endl;
        break;
    case 4:
        cout<<"These trees have neither same structure nor the data!"<<endl;
        break;
    default:
        break;
    }
    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35357274/article/details/102962920