数据结构学习记录——判断是否为同一颗二叉搜索树(题意理解、求解思路、程序搭建框架、具体函数的实现)

简介: 数据结构学习记录——判断是否为同一颗二叉搜索树(题意理解、求解思路、程序搭建框架、具体函数的实现)

题意理解

给定一个插入序列就可以唯一确定一颗二叉搜索树

但是,一颗给定的二叉搜索树却可以由多种不同的插入序列得到。

例如,按照序列{2,1,3}和{2,3,1}插入初始为空的二叉搜索树,都得到一样的结果。


问题

描述

对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。

输入样例

4 2              第一部分是两个整数:4表示插入二叉搜索树的节点数、2表示要进行比较的序列

3 1 4 2        第二部分是输入的序列                                                    

3 4 1 2        第三部分是后面输入的序列,它们全部都要与第二部分进行对比,判断是否一样

3 2 4 1

2 1

2 1

1 2

0

输出样例

Yes

No

No

求解思路

建两棵二叉树

根据两个序列分别建树,再去递归判别两棵树是否一样。

不建树

例如:

3 1 2 4 对比 3 4 1 2,3都是根结点。

那找出所有比3大的结点和所有比3小的结点,发现:


很显然,我们能看出他们是同一棵二叉搜索树了。

再来对比一组:3  1  2  4、3  2  4  1。

这一组序列并不是同一棵二叉搜索树。

建一棵树

根据结点数来建立一棵二叉搜索树T,再来判别一个序列是否与二叉搜索树T一致。

分为三部分:

  1. 搜索树表示
  2. 建立搜索树T
  3. 判别一序列是否与搜索树T一致

搜索树表示

typedef struct TreeNode* Tree;
struct TreeNode
{
  int v;
  Tree Left, Right;
  int flag;
};

程序框架搭建

int main()

{

   对每组数据

       1.读入N和L

        2.根据第一行序列建树T

       3.依据树T分别判别后面的L个序列是否能与T形成同一搜索树并输出结果。

       return 0;

}


需要设计的主要函数:

  • 读数据建立搜索树T
  • 判别一序列是否与T构成一样的搜索树
int main()
{
  int N, L, i;
  Tree T;
  scanf("%d", &N);
  while (N)
  {
    scanf("%d", &L);
    T = MakeTree(N);
    for (i = 0; i < L; i++)
    {
      if (Judge(T, N))
        printf("Yes\n");
      else
        printf("No\n");
      ResetT(T); /*清除T中的所有标记flag*/
    }
    FreeTree(T);
    scanf("%d", &N);
  }
  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 = T->Right = NULL;
  T->flag = 0;
  return T;
}
Tree Insert(Tree T, int V)
{
  if (!T)
    T = NewNode(V);
  else
  {
    if (V > T->v)
      T->Right = Insert(T->Right, V);
    else
      T->Left = Insert(T->Left, V);
  }
  return T;
}

如何判别

如何判别序列3  2  4  1是否与树T一致呢?

方法

在树T中按顺序搜索序列3  2  4  1中的每个数:

  • 如果每次搜索所经过的结点在前面均出现过,则一致
  • 否则(某次搜索中遇到前面未出现过的结点),则不一致

查找函数

check函数接受两个参数:一个二叉搜索树T和一个整数V。

函数的返回值为整型。


在二叉搜索树T中查找整数V, 如果找到了,则将该结点的标记flag设为1,并返回1;


如果没有找到,则返回0。


用递归的方式:首先判断当前结点是否被标记,如果被标记,则根据V与当前结点的大小关系,递归地查找左子树或右子树。


如果当前结点未被标记,则判断V是否等于当前结点的值, 如果等于,则将该结点的标记设为1,并返回1;

否则返回0。

int check(Tree T, int V)
{
  if (T->flag)
  {
    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)
    {
      T->flag = 1;
      return 1;
    }
    else
    {
      return 0;
    }
  }
}

判断函数

int Judge(Tree T, int N)
{
  int i, V;
  scanf("%d", &V);
  if (V != T->v)
  {
    return 0;
  }
  else
  {
    T->flag = 1;
  }
  for (i = 1; i < N; i++)
  {
    scanf("%d", &V);
    if (!check(T, V)) 
    return 0;
  }
  return 1;
}1. int J

函数有两个参数:一个是指向树的指针T,另一个是整数N。


首先从输入中读取一个整数V,如果V不等于树T的根结点值,则返回0, 表示输入的第一个数与树T不匹配。


如果V等于树T的根结点值,则将根结点的标记flag设置为1, 表示该节点已经被访问过。


接下来,用一个循环来读取剩余的N-1个整数。


对于每个整数V,函数调用check函数来检查树T中是否存在一个结点的值等于V。


如果不存在这样的节点,则返回0,表示输入的整数与树T不匹配。


如果存在这样的节点,则将该节点的标记flag设置为1, 表示该节点已经被访问过。


最后,如果所有的输入整数都能在树T中找到对应的节点,则返回1,表示输入的整数与树T匹配。


但是这段代码是存在bug的,当发现序列中的某个数与T不一致时,返回0程序结束,而不会把后面的数读完了。


那么就会误把后面的数当成下一组序列来看了,导致序列错乱了。


所以,当发现序列中的某个数与T不一致时,必须把序列后面的数都读完。

据此来改进代码:

int Judge(Tree T, int N)
 
{
  int i, V, flag = 0;
 
  /*flag:0代表目前还一致,1代表已经不一致*/
 
  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)))
      flag = 1;
  }
  if (flag) 
    return 0;
  else 
    return 1;
}

输入参数为一个指向二叉树根结点的指针T和一个整数N,表示序列的长度。


函数的返回值为1或0,表示给定的二叉树是否与序列匹配。


首先从输入中读取第一个整数V,表示序列中的第一个元素。


如果V与二叉树根结点的值不相等,则将flag(这里是函数内部的flag标记)标记为1,表示当前已经不一致了;


否则将根节点的flag(这里是结点内的flag标记,表示是否访问过)标记为1,表示已经匹配上了。


再循环读取序列中的剩余元素,每次读取一个整数V。


如果当前已经不一致了(即flag为1),则直接跳过后面的元素。


如果当前还一致,并且当前节点的值等于V,则将当前节点的flag标记为1,表示已经匹配上了;


否则将flag标记为1,表示当前已经不一致了。


循环结束后,如果flag为1,则说明序列与二叉树不匹配,返回0;


否则返回1,表示匹配成功。

其他函数

清除T中各结点的flag标记

void ResetT(Tree T) /*清除T中各结点的flag标记*/
{
  if (T->Left)
    ResetT(T->Left);
  if (T->Right)
    ResetT(T->Right);
  T->flag = 0;
}

先递归处理其左子树,再递归处理其右子树, 最后将该结点的flag标记设为0,表示清除标记。 这样,整个二叉树中所有节点的flag标记都被清除了。

释放T的空间

void FreeTree(Tree T) /*释放T的空间*/
{
  if (T->Left)
    FreeTree(T->Left);
  if (T->Right)
    FreeTree(T->Right);
  free(T);
}

end



目录
相关文章
|
21天前
|
存储 算法 数据安全/隐私保护
【Python学习篇】Python实验小练习——高级数据结构(五)
【Python学习篇】Python实验小练习——高级数据结构(五)
34 1
|
3天前
|
存储 JavaScript 前端开发
JavaScript中的对象是数据结构,存储键值对,键为字符串,值可为任意类型,包括函数(作为方法)
【6月更文挑战第25天】JavaScript中的对象是数据结构,存储键值对,键为字符串,值可为任意类型,包括函数(作为方法)。
10 2
|
22天前
|
存储 算法 C语言
数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)
数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)
24 1
|
22天前
|
存储 算法 测试技术
数据结构学习记录——树习题-Complete Binary Search Tree(题目描述、输入输出示例、数据结构的选择、核心算法、计算左子树的规模)
数据结构学习记录——树习题-Complete Binary Search Tree(题目描述、输入输出示例、数据结构的选择、核心算法、计算左子树的规模)
19 1
|
22天前
|
测试技术 C语言
数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)
数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)
15 1
|
1天前
|
人工智能 算法
程序技术好文:算法与数据结构
程序技术好文:算法与数据结构
|
22天前
|
存储
数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)
数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)
18 0
|
22天前
|
存储 算法
数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)
数据结构学习记录——图应用实例-六度空间(题目描述、算法思路、伪代码及解读、图解)
14 0
|
22天前
|
存储 算法 安全
数据结构学习记录——图应用实例-拯救007(问题描述、解题思路、伪代码解读、C语言算法实现)
数据结构学习记录——图应用实例-拯救007(问题描述、解题思路、伪代码解读、C语言算法实现)
17 0
|
3天前
|
算法 C语言
【数据结构与算法 经典例题】使用栈实现队列(图文详解)
【数据结构与算法 经典例题】使用栈实现队列(图文详解)