数据结构题目笔记-01-树 判断是否是同一棵二叉搜索树 C语言

329 阅读4分钟

引言

给定一个插入序列就可以唯一确定一颗二叉搜索树(Binary Research Tree).但是,同一颗二叉搜索树可以由多种不同的插入序列方式得到。

例如:下面的二叉搜索树:可以分别由序列{3 1 2 4}和序列{3 1 4 2}得到。

image.png

提问:对于不同的输入序列,怎么判断他们生成的是否是同一颗二叉搜索树?

方法一

解题思路

最直接的方法,分别用输入的序列构造不同的树,再同时遍历和对比这两颗二叉搜索树,最后判断是否为同一颗树。

#include <stdio.h> 
#include <malloc.h>

typedef struct TNode *Tree;
typedef int ElementType;

struct TNode{
    ElementType element;
    Tree Left,Right;
};

void FreeTree(Tree T);
Tree BuildTree(int N);
int IsSameTree(Tree T1,Tree T2);
Tree NewNode(int V);
Tree insert(Tree T,int V);

int main(void){

    int N,L,i;
    Tree T1;
    scanf("%d",&N);//结点个数/序列个数

    if (N){
        scanf("%d",&L);//要验证的树的数量
        T1=BuildTree(N);

        for(i=0;i<L;i++){
            Tree T2=BuildTree(N);
            if(IsSameTree(T1,T2)){
                printf("YES\n");
            } else{
                printf("NO\n");
            }
            FreeTree(T2);
        }

    }
    FreeTree(T1);

    return 0;
}

Tree BuildTree(int N){//创建一棵树

    Tree T;
    int i,V;
    scanf("%d",&V);//创建第一个结点
    T=NewNode(V);
    for(i=1;i<N;i++){
        scanf("%d",&V);
        T=insert(T,V);
    }
    return T;

}

Tree NewNode(int V){

    Tree T=(Tree)malloc(sizeof (struct TNode));//申请一个结点空间
    T->element=V;
    T->Left=NULL;
    T->Right=NULL;

    return T;
}

Tree insert(Tree T,int V){

    if(!T){
        T= NewNode(V);//如果树是空的,那么创建第一个结点
    } else if(V<T->element){
        T->Left= insert(T->Left,V);//树不是空的,并且传入的值 小于 当前结点值,那么这个结点左子树等于左子树中插入后得到的新的左子树
    } else if(V>T->element){
        T->Right=insert(T->Right,V);//树不是空的,传入值 大于 当前结点值,那么这个结点右子树等于右子树插入后得到的右子树(直到插入到最下面结点为止)
    }

    return T;

}

int IsSameTree(Tree T1,Tree T2){//核心对比函数

    int flag;//标记是否相同,0表示一致,1表示不一致

    if(T1==NULL && T2==NULL){//同时为空,认为它们相同
        flag=1;
    } else if(T1 && T2){//同时不为NULL,进一步分别比较左子树和右子树
        if(T1->element == T2->element){
            flag= (IsSameTree(T1->Left,T2->Left) && IsSameTree(T1->Right,T2->Right));
        } else{
            flag=0;
        }
    } else{//其他情况,返回不相同(比如T1或者T2中其中一个为NULL)
        flag=0;
    }
    return flag;
}

void FreeTree(Tree T){//释放内存
    if(T->Left){
        FreeTree(T->Left);
    }
    if(T->Right){
        FreeTree(T->Right);
    }
    free(T);
}

方法二

解题思路

只构造一棵二叉搜索树,然后把要验证的序列跟已经构造好的二叉搜索树进行遍历对比。具体是让序列中每一个需要比较的元素在二叉搜索树中走一遍,知道找到相同数值的结点。如果图中走到某一个没有访问过的结点,并且这个结点的值与当前结点元素的值不相同,那么可以判断这个序列能构造的不是同一颗二叉搜索树。

typedef struct TreeNode *Tree;//创建一个结点指针

struct TreeNode{
    int v;
    Tree Left,Right;
    int flag;//用来标记遍历的时候是否走过此节点。0表示没有走过,1表示走过了
};


Tree MakeTree(int N);
void ResetTree(Tree T);
void FreeTree(Tree T);
Tree NewNode(int V);
Tree insert(Tree T,int V);
int check(Tree T,int V);
int Judge(Tree T,int N);


int main(void){

    int N,L,i;
    Tree T;

    scanf("%d",&N);//树的结点个数

    if(N){
        scanf("%d",&L);//需要检验的树的个数
        T=MakeTree(N);
        for(i=0;i<L;i++){
            if(Judge(T,N)){
                printf("YES\n");
            } else{
                printf("NO\n");
                ResetTree(T);//重置树的flag使其变为0
            }
        }
        FreeTree(T);
        scanf("%d",&N);//当N=0的时候,表示读入结束
    }

    return 0;

}


Tree MakeTree(int N){// 创建搜索树

    Tree T;
    int i,V;
    scanf("%d",&V);//创建第一个结点
    T=NewNode(V);
    for(i=1;i<N;i++){
        scanf("%d",&V);
        T=insert(T,V);
    }
    return T;
}


Tree NewNode(int V){//创建一个新的树节点

    Tree T=(Tree)malloc(sizeof (struct TreeNode));//申请一个结点空间
    T->v=V;
    T->Left=NULL;
    T->Right=NULL;
    T->flag=0;//没被访问过则为0,访问过了则设为1

    return T;
}

Tree insert(Tree T,int V){//将新的结点插入到树中

    if(!T){
        T= NewNode(V);//如果树是空的,那么创建第一个结点
    } else if(V<T->v){
        T->Left= insert(T->Left,V);//树不是空的,并且传入的值 小于 当前结点值,那么这个结点左子树等于左子树中插入后得到的新的左子树
    } else if(V>T->Right){
        T->Right=insert(T->Right,V);//树不是空的,传入值 大于 当前结点值,那么这个结点右子树等于右子树插入后得到的右子树(直到插入到最下面结点为止)
    }

    return T;
}



//核心函数   
int Judge(Tree T,int N){
    int i,V;
    int flag=0;//0表示读得过程中是一致的,1表示不一致(PS:这里的flag与定义的TreeNode里面的flag名字相同,但是表示不一样的内容)

    scanf("%d",&V);
    if(V!=T->v){
        flag=1;
    } else{
        T->flag=1;
    }
    for(i=1;i<N;i++){
        scanf("%d",&V);
        if((!flag) && (!check(T,V))){// 如果发现序列中的某个数与T不一致时,必须把序列后面的数都读完
            flag = 1;
        }
    }
    if(flag){
        return 0;
    } else{
        return 1;
    }

}

int check(Tree T,int V){//判断是否为同一搜索树

    if(T->flag){//如果flag的值为1,那么代表这个结点已经走过了,那么需要往下面走
        if(V<T->v){//这个判断的值小于结点值,那么往左边判断
            return check(T->Left,V);
        } else if(V>T->v){//值大于结点值,那么往右边判断
            return check(T->Right,V);
        } else{//如果结点走过了但是又来了一个新的值,那么说明传入的序列中有两个值都相等,此时认为不是同一搜索树
            return 0;
        }
    } else{
        if(V==T->v){//结点没走过,并且恰好就是这个结点,那么把这个结点标注并且返回 1
            T->flag=1;
            return 1;
        } else{
            return 0;
        }
    }
}


void ResetTree(Tree T){//重置搜索树中的flag

    if(T->Left){
        ResetTree(T->Left);
    }
    if(T->Right){
        ResetTree(T->Right);
    }
    T->flag=0;
}


void FreeTree(Tree T){//释放内存

    if(T->Left){
        FreeTree(T->Left);
    }
    if(T->Right){
        FreeTree(T->Right);
    }
    free(T);
}

如果大家有好的想法,欢迎在评论区交流讨论... 原创不易,转载请标明出处