是否同一棵二叉搜索树

是否同一棵二叉搜索树

给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。

输入格式:

输入包含若干组测试数据。每组数据的第1行给出两个正整数N (≤)和L,分别是每个序列插入元素的个数和需要检查的序列个数。第2行给出N个以空格分隔的正整数,作为初始插入序列。最后L行,每行给出N个插入的元素,属于L个需要检查的序列。

简单起见,我们保证每个插入序列都是1到N的一个排列。当读到N为0时,标志输入结束,这组数据不要处理。

输出格式:

对每一组需要检查的序列,如果其生成的二叉搜索树跟对应的初始序列生成的一样,输出“Yes”,否则输出“No”。

输入样例:

4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0

输出样例:

Yes
No
No

 

解题思路

  对于输入的两组不相同的序列,来判断这两个序列所构成的树是否为同一颗BST,这里提供两种不同的解法。

  第一种是最直接,最容易想到的,就是用这两组序列来分别构造出两颗不同的二叉树,再同时对这两颗树进行遍历,来判断是否为同一颗二叉树。

  程序的框架就是,先按第一行给出的序列来构造出一颗树T1,这颗树是需要和后面的L颗树进行比较的。然后循环L次,每次输入一组序列,用同样的方法来构造这组序列对应的BST,然后再与T1比较,如果这两颗BST是一样的,那么输出"Yes",否则输出"No"。

  当然,我们还需要创建一个构造树的函数,以及比较两颗树是否相同的函数。

  AC代码如下:

 1 #include <cstdio>
 2 
 3 struct TNode {
 4     int data;
 5     TNode *left, *right;
 6 };
 7 
 8 TNode *createTree(int n);
 9 TNode *insert(TNode *T, int v);
10 bool isSameTree(TNode *T1, TNode *T2);
11 void freeTree(TNode *T);
12 
13 
14 int main() {
15     int n;
16     scanf("%d", &n);
17     
18     while (n) {
19         int m;
20         scanf("%d", &m);
21         
22         TNode *T1 = createTree(n);  // T1与后面的L颗树比较 
23         
24         while (m--) {
25             TNode *T2 = createTree(n);
26             printf("%s\n", isSameTree(T1, T2) ? "Yes" : "No");
27             freeTree(T2);           // 与T1比较完后,把构造的树释放掉 
28         }
29         
30         freeTree(T1);               // 与L颗树比较完后,再释放T1 
31         
32         scanf("%d", &n);
33     }
34     
35     return 0;
36 }
37 
38 TNode *createTree(int n) {  //构造树的函数,n代表树的节点个数 
39     TNode *T = NULL;
40     for (int i = 0; i < n; i++) {
41         int v;
42         scanf("%d", &v);
43         
44         T = insert(T, v);   // 把输入的值按BST的规则插入到树中 
45     }
46     
47     return T;               // 返回这颗树的根节点 
48 }
49 
50 TNode *insert(TNode *T, int v) {                        // 该递归函数的作用是把值插在给定的树中,并返回插入后的新树的根节点 
51     if (T == NULL) {                                    // 如果根节点为空,那么生成一个节点,插入的值变成根节点 
52         T = new TNode;
53         T->data = v;
54         T->left = T->right = NULL;
55     }
56     else {
57         if (v < T->data) T->left = insert(T->left, v);  // 如果插入的值比根节点的值小,则把值插在左子树,并返回左子树的根节点 
58         else T->right = insert(T->right, v);            // 如果插入的值比根节点的值大,则把值插在右子树,并返回右子树的根节点
59     }
60     
61     return T;                                           // 返回根节点 
62 }
63 
64 bool isSameTree(TNode *T1, TNode *T2) { // 核心函数,用来判断两颗树是否相同 
65     bool ret;
66     
67     if (T1 == NULL && T2 == NULL) {     // 如果两颗树都为空,这两颗树肯定是一样的(都为空) 
68         ret = true;
69     }
70     else if (T1 && T2) {                // 如果两颗树的根节点都存在 
71         if (T1->data == T2->data) {     // 如果根节点的值相同 
72             
73             // 还需要判断左两颗树的左子树是否相同,右子树是否相同,而判断左右子树是否相同和判断两颗树是否相同的方法是一样的,所以使用递归
74             // 只有两颗树的左右子树都相同,这两颗树才会相同 
75             ret = isSameTree(T1->left, T2->left) && isSameTree(T1->right, T2->right);
76         }
77         else {                          // 如果根节点的值不同,那么这两颗树不相同 
78             ret = false;
79         }
80     }
81     else {                              // 如果一颗树为空,而另外一颗树不为空,那么这两棵树肯定是不一样的
82         ret = false;
83     }
84     
85     return ret;
86 }
87 
88 void freeTree(TNode *T) {   // 释放节点,本质就是后序遍历 
89     if (T) {
90         freeTree(T->left);  // 先把左子树的节点释放 
91         freeTree(T->right); // 再把右子树的节点释放 
92         delete T;           // 最后才释放根节点 
93     }
94 }

  下面的方法是何老师给出的。

  这个方法只需要构造出一颗树,也就是构造第一行序列的那颗树。而后面L组序列不需要构造树。

  比较树的方法是,让比较的序列的每一个元素去走一遍那颗树,直到找到相同值得那个节点。如果途中经过某一个之前没有访问过节点,并且这个节点的值和这个元素的值不相同,那么就可以判定这两组序列所构造的树不是同一颗二叉搜索树,之后的元素不再需要走这颗树了。

  所以,我们的节点要包含一个标识域,用来记录该节点有没有被访问过。

  AC代码如下:

  1 #include <cstdio>
  2 
  3 struct TNode {
  4     int data;
  5     TNode *left, *right;
  6     bool flag;  // 标识域,用来标记该节点在比较的时候有没有被访问过 
  7 };
  8 
  9 TNode *createTree(int n); 
 10 TNode *insert(TNode *T, int v);
 11 bool isSameTree(TNode *T, int n);
 12 bool judge(TNode *T, int v);
 13 void reset(TNode *T);
 14 void freeTree(TNode *T);
 15 
 16 
 17 int main() {
 18     int n;
 19     scanf("%d", &n);
 20     
 21     while (n) {
 22         int m;
 23         scanf("%d", &m);
 24         
 25         TNode *T = createTree(n);
 26         
 27         while (m--) {
 28             printf("%s\n", isSameTree(T, n) ? "Yes" : "No");
 29             reset(T);  // 当与一个序列比较完后,别忘了重新把标识域赋值为false,再与下一组序列作比较 
 30         }
 31         
 32         freeTree(T);
 33         
 34         scanf("%d", &n);
 35     }
 36     
 37     return 0;
 38 }
 39 
 40 TNode *createTree(int n) {
 41     TNode *T = NULL;
 42     for (int i = 0; i < n; i++) {
 43         int v;
 44         scanf("%d", &v);
 45         
 46         T = insert(T, v);
 47     }
 48     
 49     return T;
 50 }
 51 
 52 TNode *insert(TNode *T, int v) {
 53     if (T == NULL) {
 54         T = new TNode;
 55         T->data = v;
 56         T->left = T->right = NULL;
 57         T->flag = false;  // 创建节点的时候同时把标识域赋值为false 
 58     }
 59     else {
 60         if (v < T->data) T->left = insert(T->left, v);
 61         else T->right = insert(T->right, v);
 62     }
 63     
 64     return T;
 65 }
 66 
 67 bool isSameTree(TNode *T, int n) {
 68     bool ret = true;                // 用来记录上一个元素比较的结果 
 69     for(int i = 0; i < n; i++) {    // 序列中的每一个节点都要走一遍T1 
 70         int v;
 71         scanf("%d", &v);
 72         if (ret) ret = judge(T, v); // 如果ret == true,那么下一个元素还要继续判断。如果在比较中已经发现两颗树不可能相同,那么就之后的元素都不需要再走一遍T1了,但之后的元素还是要输入 
 73     }
 74     
 75     return ret;
 76 }
 77 
 78 bool judge(TNode *T, int v) {
 79     bool ret;
 80     if (T->flag) {                                  // 如果该节点之前被访问过 
 81         if (v < T->data) ret = judge(T->left, v);   // 如果元素的值比根节点的值小,则走左子树 
 82         else ret = judge(T->right, v);              // 如果元素的值比根节点的值大,则走右子树 
 83     }
 84     else {                                          // 如果该节点之前没有访问过 
 85         if (v == T->data) {                         // 但根节点的值与元素的值相同 
 86             T->flag = true;                         // 则flag赋值为true表示该节点被访问过了 
 87             ret = true;
 88         }
 89         else {                                      // 该节点没有被访问过且根节点的值与元素的值不相同 
 90             ret = false;                            // 返回false表面这两个序列不可能构成同一颗BST 
 91         }
 92     }
 93     
 94     return ret;
 95 }
 96 
 97 void reset(TNode *T) {      // 先序遍历,把T1的每一个节点的标识域赋值为false 
 98     if (T) {
 99         T->flag = false;    // 先把根节点的标识域赋值为false 
100         reset(T->left);     // 再把左子树的每一个节点的标识域赋值为false
101         reset(T->right);    // 再把右子树的每一个节点的标识域赋值为false
102     }
103 }
104 
105 void freeTree(TNode *T) {
106     if (T) {
107         freeTree(T->left);
108         freeTree(T->right);
109         delete T;
110     }
111 }

 

参考资料

  浙江大学——数据结构:https://www.icourse163.org/course/ZJU-93001?tid=1461682474

posted @ 2021-03-30 12:32  onlyblues  阅读(417)  评论(0编辑  收藏  举报
Web Analytics