第五章 树与二叉树总结

树结构是一类重要的非线性数据结构

1、树的定义树(Tree)是n(n>=0)个结点的有限集,它或为空树(n=0);或为非空树

对于非空树:

(1)有且仅有一个称之为根的结点;

(2)除根结点以外的其余结点可分为m(m>0)个互不相交的有限集T1,T2,...,Tm,其中每个集合本身又是一颗树,并且称为根的子树(SubTree);

2、树的基本术语

(1)结点:树中的一个独立单元。

(2)结点的度:结点拥有的子树数称为结点的度。

(3)树的度:树的度是树内各结点的最大值

(4)叶子:度为0的结点称为叶子或终端结点。

(5)非终端结点:度不为0的结点称为非终端结点或分支结点。除根结点之外,非终端结点也称为内部结点。

(6)双亲和孩子、(7)兄弟、(8)祖先、(9)子孙

(10)层次:结点的层次从根开始定义起,根为第一层,根的孩子为第二层。

在这章老师布置给我们的作业是:PTA上的一道关于树的遍历,输出叶子的序号;

题目如下:

7-1 List Leaves (30 分)

Given a tree, you are supposed to list all the leaves in the order of top down, and left to right.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤10) which is the total number of nodes in the tree -- and hence the nodes are numbered from 0 to N−1. Then N lines follow, each corresponds to a node, and gives the indices of the left and right children of the node. If the child does not exist, a "-" will be put at the position. Any pair of children are separated by a space.

Output Specification:

For each test case, print in one line all the leaves‘ indices in the order of top down, and left to right. There must be exactly one space between any adjacent numbers, and no extra space at the end of the line.

Sample Input:

8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6

Sample Output:

4 1 5

解题思路:这道题的一个坑点是会先入为主认为1便是根节点,其实不然。这道题有个巧妙的地方就是其实题目中没出现的(即不是孩子的序号)便是根结点。还有这道题要用到队列思想。如何去遍历输出叶子结点。代码如下:
 1 #include<iostream>
 2 using namespace std;
 3
 4 int n ;
 5 bool vis[15];
 6 struct node{
 7     int lchild;
 8     int rchild;
 9 }tree[15];
10 int sz[15];
11 int head = 0 , rear = 0;   //队列的序号
12 int main()
13 {
14     cin>>n;
15     char l , r;
16     for(int i = 0 ; i < n ;i++)
17     {
18         cin>>l>>r;
19         if(l!=‘-‘)     //如果有左孩子
20         {
21             tree[i].lchild = l - ‘0‘;     //赋值
22             vis[tree[i].lchild] = 1;   //表示该是孩子且访问过;
23         }else
24         {
25             tree[i].lchild = -1;    //否则赋值为-1 ,表示没左孩子;
26         }
27
28         if(r!=‘-‘)    //如果有右孩子;
29         {
30             tree[i].rchild = r - ‘0‘;   //赋值;
31             vis[tree[i].rchild] = 1;   //表示是孩子且访问过;
32         }else
33         {
34             tree[i].rchild = -1;   //否则赋值为-1,表示没右孩子;
35         }
36     }
37     int root;
38         for(int i = 0 ; i < n ;i++)
39     {
40          if(vis[i]==0)
41          {
42              root = i ;    //找出根;
43              break;
44          }
45     }
46     int leaves = 0;
47      sz[rear++] = root;   //让根先入队;
48      while(rear - head > 0 )
49     {
50         int num = sz[head++];  //取出队列的元素
51         if (tree[num].lchild == -1 && tree[num].rchild == -1) {    //输出叶节点
52
53             if (leaves)
54
55                 printf(" ");
56
57             printf("%d", num);
58
59             ++leaves;
60
61         }
62
63         if (tree[num].lchild != -1) {        //如果存在,左儿子入队
64
65             sz[rear++] = tree[num].lchild;   //入队;
66
67         }
68
69         if (tree[num].rchild != -1) {        //如果存在,右儿子入队
70
71             sz[rear++] = tree[num].rchild;//入队;
72
73         }
74
75
76      }
77
78 }

此外,我们还做了两道实践题,一道是深入虎穴(老师周四在实验室引导我们一步一步去解决出来),老师用的是孩子表示法,而我自己用是vector;

,一道是树的同构;

深入虎穴 题目如下:

著名的王牌间谍 007 需要执行一次任务,获取敌方的机密情报。已知情报藏在一个地下迷宫里,迷宫只有一个入口,里面有很多条通路,每条路通向一扇门。每一扇门背后或者是一个房间,或者又有很多条路,同样是每条路通向一扇门…… 他的手里有一张表格,是其他间谍帮他收集到的情报,他们记下了每扇门的编号,以及这扇门背后的每一条通路所到达的门的编号。007 发现不存在两条路通向同一扇门。

内线告诉他,情报就藏在迷宫的最深处。但是这个迷宫太大了,他需要你的帮助 —— 请编程帮他找出距离入口最远的那扇门。

输入格式:

输入首先在一行中给出正整数 N(<10?5??),是门的数量。最后 N 行,第 i 行(1≤i≤N)按以下格式描述编号为 i 的那扇门背后能通向的门:

K D[1] D[2] ... D[K]

其中 K 是通道的数量,其后是每扇门的编号。

输出格式:

在一行中输出距离入口最远的那扇门的编号。题目保证这样的结果是唯一的。

输入样例:

13
3 2 3 4
2 5 6
1 7
1 8
1 9
0
2 11 10
1 13
0
0
1 12
0
0

输出样例:

12

解题思路:这道题就是说给你N个数,代表N个结点,第i行,每行有K个数;
让你找树深度最深的那个结点;题目注意要先找出根,其实题目没出现的数字就是根,所以我
们可以把孩子都标记起来,没有被标记的就是根;我们可以先用一个vector把孩子都放在共同的父亲上,
然后孩子的深度都比父亲多一;代码如下:

代码如下:

 1 #include<iostream>
 2 #include<vector>
 3 #include<queue>
 4 using namespace std;
 5
 6 int N;
 7 int K;
 8 int tmp;
 9 bool vis[100005];//用来标记点是否出现过,方便找出根结点;
10 vector<int>v[100005];   //动态数组,将相同父亲的孩子放入父亲的vector中;
11 int depth[100005];  //用来记录每个数字的深度;
12 int root;
13 int tp;
14 int maxnn = -1;   //用来找深度最大的;
15 int ans = -1;    //用来记录答案;
16 int main()
17 {
18     queue<int>q;
19     scanf("%d",&N);
20     for(int i = 1 ; i <= N ;i++)
21     {
22         scanf("%d",&K);
23         while(K--)
24         {
25             scanf("%d",&tmp);
26             v[i].push_back(tmp);  //将孩子结点放入父亲的数组中;
27             vis[tmp] = 1;    //标记该点是孩子结点;
28         }
29     }
30
31         for(int j = 1 ; j <= N ;j++)
32         {
33             if(vis[j]!=1)
34             {
35                 root = j ;    //找到根结点;
36             }
37         }
38         q.push(root);    //将根结点入队;
39         depth[root] = 1;  //根的深度为1;
40         while(!q.empty())
41         {
42             tp = q.front();
43             q.pop();
44             for(int j = 0 ; j < v[tp].size();j++)
45             {
46                 depth[v[tp][j]] = depth[tp]+1;  //每个孩子的深度比父亲多一;
47                 q.push(v[tp][j]);  //将孩子入队;
48             }
49         }
50
51         for(int j = 1 ; j <= N ;j++)
52         {
53             if(depth[j]>maxnn)
54             {
55                 maxnn = depth[j];  //找最大深度的;
56                 ans = j;     //找到答案;
57             }
58         }
59         printf("%d\n",ans);
60
61     return 0;
62 }

另一道是树的同构,树的同构是一个全新的概念,一开始是不知道树的同构是什么意思,觉得很复杂,不知道如何下手,但是静下心来想,其实它可以分为如下几种情况:

假设它同构:

1)两颗都为空树;

2)只有根,根的数值想等;

3)两颗树对应的左孩子相等,右孩子相等;

4)两颗树对应的左孩子等于右孩子;

其余情况都不符合;

题目如下:

输入格式:

输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N−1编号);随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。如果孩子结点为空,则在相应位置上给出“-”。给出的数据间用一个空格分隔。注意:题目保证每个结点中存储的字母是不同的。

输出格式:

如果两棵树是同构的,输出“Yes”,否则输出“No”。

输入样例1(对应图1):

8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -

输出样例1:

Yes

输入样例2(对应图2):

8
B 5 7
F - -
A 0 3
C 6 -
H - -
D - -
G 4 -
E 1 -
8
D 6 -
B 5 -
E - -
H - -
C 0 2
G - 3
F - -
A 1 4

输出样例2:

No

代码如下:
 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5
 6
 7 struct node{
 8     char data;
 9     int l;
10     int r;
11 }tree1[10],tree2[10];//定义树的结构体;
12 int root1 , root2;
13 int vis[10];//作为标记,方便找到根;
14 int creat_tree( node tree[])
15 {
16     int root = -1;   //先将根标记为-1;
17     char data , l , r;
18     int n ;
19     scanf("%d",&n);
20     getchar();
21     for(int i = 0 ; i < n ;i++)
22     {
23         vis[i] = 0;      //先将所有标记初始化为0;
24     }
25     for(int i = 0 ; i < n ; i++)
26     {
27         scanf("%c %c %c",&data,&l,&r);
28         tree[i].data = data;
29         if(l == ‘-‘)
30         tree[i].l = -1;    //如果没有左孩子,则标记为-1;
31         else
32         {
33             tree[i].l = l -‘0‘;    //有左孩子,则赋值,
34             vis[tree[i].l] = 1;    //且标记为访问过;
35         }
36         if(r==‘-‘)     //如果没有右孩子;
37         tree[i].r = -1;  //标记右孩子为-1;
38         else
39         {
40             tree[i].r = r - ‘0‘;   //有右孩子,则赋值,
41             vis[tree[i].r] = 1;  //且标记为访问过;
42         }
43         getchar();
44     }
45     for(int i = 0 ; i < n ; i++)
46     {
47         if(vis[i]==0)
48         {
49             root = i;    //如果有根,则赋值;(如果在这里找不到根,则root==-1)
50         }
51     }
52     return root;
53
54 }
55 int Istonggou(int root1 ,int root2)
56 {
57     if(root1 == -1 &&root2 ==-1)   //两颗都为空树,满足同构;
58     {
59             return 1;
60     }
61     else
62     if( root1 == -1 && root2 != -1) return 0;   //一颗为空树,一颗不为空树,不满足
63     else
64     if( root1 != -1 && root2 == -1) return 0;  //一颗为空树,一颗不为空树,不满足
65     else
66     if(tree1[root1].data!=tree2[root2].data)   //两颗树根的数值不同;不满足;
67     {
68         return 0;
69     }
70     //其余情况其实总体来说就分为两大类
71     //1)(1)tree1的左子树和tree2的左子树相同; (2)tree1的右子树和tree2的右子树相同;
72     //2)(1)tree1的左子树和tree2的右子树相同;(2)tree1的右子树和tree2的左子树相同;
73     return (Istonggou(tree1[root1].l,tree2[root2].l)&&Istonggou(tree1[root1].r,tree2[root2].r))||
74     (Istonggou(tree1[root1].l,tree2[root2].r)&&Istonggou(tree1[root1].r,tree2[root2].l));
75 }
76
77 int main()
78 {
79     int flag = 0;
80     root1 = creat_tree(tree1);
81     root2 = creat_tree(tree2);
82     flag = Istonggou(root1,root2);
83     if(flag)   //如果满足同构树的条件;
84     printf("Yes\n");
85     else
86     printf("No\n");
87     return 0;
88 }

其实除了这几道题外,还学习了树的相关性质,二叉树的性质(挺重要的,这里便不赘述了)
二叉树存储方式:顺序存储和链式存储(二叉链表,三叉链表)

二叉树几种表示方法:如孩子表示法,双亲表示法,孩子双亲表示法

还学习了哈夫曼树,其实哈夫曼树是一个很重要的东西,更重要的是其思想。原本想尝试着在这篇博客中解释哈夫曼树,(其之前建的树不一样,之前的树都是从上往下建,而哈夫曼树是从下往上建,这样建也是有其原因)。但是写得有点匆忙,等后期补充吧。

这一章的学习:其实感觉学的不是那么扎实,感觉自己对树的很多操作还是不是那么熟,不是那么能灵活运用,感觉还有待提高。这周也课外学了线段树和树状数组(这其实是寒假的时候师兄教的内容了,但现在重新学牢),感觉树还是有很多便捷之处。

下一章的学习是图,其实图是很重要的,很多人对图论的知识也是不是那么的熟悉,对于图我也只懂得最小生成树和最短路径,希望在老师的讲解下,能灵活运用!

原文地址:https://www.cnblogs.com/yewanting/p/10810191.html

时间: 2024-10-12 20:46:09

第五章 树与二叉树总结的相关文章

第五章 树和二叉树

上章回顾 单链表的基本操作,包括插入.删除以及查找 双向链表和循环链表的区别 [email protected]:Kevin-Dfg/Data-Structures-and-Algorithm-Analysis-in-C.git 第五章 第五章 树和二叉树 树和二叉树 [email protected]:Kevin-Dfg/Data-Structures-and-Algorithm-Analysis-in-C.git 预习检查 什么是二叉树 树的遍历有哪几种方式 树有哪些应用 [email pr

数据结构——第五章 树与二叉树

树是一对多的结构 结点:树的小圆圈 度:结点有多少个分叉 叶子结点:结点的度为0 双亲:parent 孩子:child 二叉树:树的度不超过2 满二叉树:每一层都是满的 完全二叉树:除了最后一层都是满的,最后一层左边都是齐全连续的. 性质1:对一颗二叉树,第i层最多有2的i-1次方个 性质2:对一颗二叉树,最多有2的i次方-1个 性质3:n0=n2+1 n0+n1+n2=n(n0--结点度为0的个数,n2--结点度为2的个数) 性质4:具有n个结点的完全二叉树深度为 math.floor(log

数据结构学习之第7章 树和二叉树

数据结构学习之第7章 树和二叉树 0x7.1.1 树的基本概念 ?1.树的定义 ? 树是由n(n>=0)个结点(或元素)组成的有限集合(记为T) ? 如果n>0,这n个结点中有且仅有一个结点作为树的根结点,简称为根,其余结点可分为m(m>=0)个互不相交的有限集\[T_{1}T_{2}\cdots T_{m}\],其中每个子集又是一棵符合定义的子树,称为根结点的子树. 知识点:由树的定义我们可以看出来树的结构是递归的 ?2.树的逻辑表示法 ? 1.树形表示法 ? 2.文氏图表示法 ? 3

数据结构期末复习第六章树和二叉树

知识点: 先序遍历二叉树规则:根-左-右 1.访问根结点 2.先序遍历左子树 3.先序遍历右子树 中序遍历二叉树规则:左-根-右 1.先中序遍历左子树 2.再访问根节点 3.最后访问中序遍历右子树 后序遍历二叉树规则:左-右-根 1.后序遍历左子树 2.后序遍历右子树 3.访问根结点 1.  一棵二叉树的先序遍历结果为ABCDEF,中序遍历结果为CBAEDF,则后序遍历结果为(A)A. CBEFDA                       B. FEDCBAC. CBEDFA        

第5章 树与二叉树学习小结

前几章学习的基本都是线性的数据结构,就有顺序存储结构和链式存储结构,而这一章“树”结构是一类非线性数据结构,跟之前就有不同的点,但是,树的存储结构还是可以通过找到元素之间逻辑关系,采用类似线性表的方式,按照结点之间的逻辑关系放到线性存储中. 这部分主要学习到二叉树的内容,二叉树有好几个性质,我想这些性质很重要,有时候在解决问题,它能够帮助理解这棵树比较抽象的结构层次,这是我在理解代码时候体会到的.二叉树存储结构跟遍历有很大的关系,遍历的结果是将非线性结构的树中结点排成一个线性序列. 这是二叉链表

数据结构-王道2017-第4章 树与二叉树-树、森林

1.树的存储结构有多种,既可以采用顺序存储结构,也可以采用链式存储结构,都要求能唯一地反映出树中各结点之间的逻辑关系,三种常用的存储结构 1)双亲表示法 采用一组连续空间来存储每个结点,同时在每个结点中增设一个伪指针,指示其双亲节点在数组中的位置,根节点下标为0,其伪指针域为-1. #define MAX_TREE_SIZE 100 //树中最多结点数 typedef struct{ //树的结点定义 ElemType data; //数据元素 int parent; //双亲位置域 }PTNo

数据结构实验五:树和二叉树

一.实验目的 巩固树和二叉树的相关知识,特别是二叉树的相关内容.学会运用灵活应用. 1.回树和二叉树的逻辑结构和存储方法,清楚掌握树和二叉树的遍历操作. 2.学习树的相关知识来解决实际问题. 3.进一步巩固程序调试方法. 4.进一步巩固模板程序设计. 二.实验时间 准备时间为第10周到第12前半周,具体集中实验时间为12周周四.2个学时. 三..实验内容 1.自己设计一个二叉树,深度最少为4,请递归算法分别用前序.中序.后序遍历输出树结点. 2.写程序判定出六枚硬币中的一枚假硬币.参照课本P13

数据结构——第三章树和二叉树:03树和森林

1.树的三种存储结构: (1)双亲表示法: #define MAX_TREE_SIZE 100 结点结构: typedef struct PTNode { Elem data; int parent; //双亲位置域 } PTNode; (2)孩子双亲链表表示法: typedef struct PTNode { Elem data; int parent; //双亲位置域 struct CTNode* nextchild; } *ChildPtr; (3)树的二叉链表(孩子-兄弟)存储表示法:

第五章——树

这两周我们主要学到了有关树和二叉树的用法,掌握了二叉树的遍历的基本操作. 1.树的定义及特点 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的.它具有以下的特点:(1) 每个节点有零个或多个子节点: (2) 没有父节点的节点称为根节点: (3) 每一个非根节点有且只有一个父节点: (4) 除了根节点外,每个子节点可以分为多个不相交的子树. 2.关于树的官方语言 结点的度:结点拥有的子树的数目.叶子:度为零的结点