第五章的内容是树以及二叉树。
1、树的定义:1)有且只有一个根结点。 2)除根结点以外的结点互不相交。
树的结构定义是递归形式,于是在创建遍历等多个基本操作都会用到递归。
2、树的表示方法:嵌套集合、线性表、凹入表示
3、树的基本术语:
1)结点
2)结点的度:结点拥有的子树
3)树的度:各结点度的最大值
4)叶子
5)树的深度:树中结点的最大层次
6)完全二叉树:在存储时可通过对编号的计算找到父、子结点的对应编号
7)满二叉树(深度为K,且含有2的k次方-1个结点)
4、二叉树(结构最简单、规律性强,所有树都可以化成唯一的二叉树)
1)二叉树每个结点至多有两颗子树
2)性质:第i层至多有2的i-1次方个结点;深度为k的二叉树至多有2的k-1次方个结点;对任意一颗二叉树其终端结点为n0,度为2的结点树为n2,则n0=n2+1;
5、二叉树的存储结构(结点间的关系蕴含在存储位置 不容易实现增加删除)
1)顺序存储结构(线性,某个结点的父/子结点,按照完全二叉树down right 的编号利用下标计算)
但是遇到稀疏的树,会造成空间的极大浪费
2)链式存储结构(在含有n个结点的二叉链表中必有2n个链域,有n-1个非空,则有2n-(n-1)个空链域)
//结点的定义 typedef struct bitnode{ telemtype data; struct bitnode *lchild,*rchild; //递归 }bitnode, *bitree;
6、遍历
1)先序遍历 (根 左 右)
void preorder( bitree t ){ if ( t == null ) return; cout<< t->data <<" "; preorder ( t->lchild ); preorder ( t->rchild ); }
2)中序遍历 (左 根 右)
3)后序遍历 (右 左 根)
4)层次遍历
void level (bitree t){ queue q; bitree p; while(!q.empty()){ p=q.pop(); if(p!=null){ cout<<p->data<<" "; q.push(p->lchild); q.push(p->rchild); } } } //采用队列来遍历
7、二叉树的建立
void creatbitree(bitree &t){ cin>>ch; if (ch=='#') { t=null; retuen;//强行终止} else { t=new bitnode; t->data=ch; createbitree (t->lchild); createbitree (t->rchild); } }
8、深入虎穴的题目
著名的王牌间谍 007 需要执行一次任务,获取敌方的机密情报。已知情报藏在一个地下迷宫里,迷宫只有一个入口,里面有很多条通路,每条路通向一扇门。每一扇门背后或者是一个房间,或者又有很多条路,同样是每条路通向一扇门…… 他的手里有一张表格,是其他间谍帮他收集到的情报,他们记下了每扇门的编号,以及这扇门背后的每一条通路所到达的门的编号。007 发现不存在两条路通向同一扇门。
内线告诉他,情报就藏在迷宫的最深处。但是这个迷宫太大了,他需要你的帮助 —— 请编程帮他找出距离入口最远的那扇门。
1 #include <iostream> 2 #include <queue> 3 using namespace std; 4 5 typedef struct { 6 int doors; //门的数量 7 int *p; //指向后面的门的编号序列 8 }node; 9 10 int input(node *a, int n); 11 int level(node *a, int r); 12 13 int main() 14 { 15 node *a; //a用于存储整棵树 为啥是*a?? 16 int n; //门的数量 17 int root; 18 int t; 19 20 cin >> n; 21 a = new node[n + 1]; //为a开辟一个新的空间 大小为n(门 的数量) +1 1是啥?? 因为门牌号从1开始 22 23 root = input(a, n); 24 t = level(a, root); 25 cout << t << endl; 26 27 return 0; 28 } 29 30 int input(node *a, int n) { //读入n扇门的信息给a数组,返回根所在的门牌号 31 32 int i, j; 33 bool *vi; //为了找根 34 vi = new bool[n + 1]; 35 for (i = 1; i <= n; ++i) { //初始化vi 数组的全部元素为false 36 vi[i] = false; 37 } 38 39 for (i = 1; i <= n; ++i) { //读入n扇门的信息 40 cin >> a[i].doors; //直接读入到node里面的结构体的door的数量 41 42 if (a[i].doors != 0) { 43 a[i].p = new int[a[i].doors]; //当a[i].doors 的数量不为0时 开辟一个大小为门的数量的空间 44 45 for (j = 1; j <= a[i].doors; ++j) { 46 cin >> a[i].p[j-1]; //这是啥?? 因为假如门的数量为4,则开辟4个空间,那么如果j 从1 开始就不够空间,所以j 从0 开始 读入会被通向的门 47 vi[a[i].p[j-1]] = true; //令被通向的门变为1,则可以找到根 48 } 49 50 } 51 else //door 为空 52 a[i].p = NULL; 53 } 54 55 for (i = 1; i < n; ++i) { //找到根节点 56 if (!vi[i]) return i; 57 } 58 } 59 60 int level(node *a, int r) 61 {//从a [r] 开始对a进行层次遍历,并返回遍的最后一个节点的编号 62 queue<int> q; 63 64 int t; 65 q.push(r); 66 67 while (!q.empty()) { //非空 68 t = q.front(); //那队列的队头元素 69 q.pop(); //chu队 70 71 if (a[t].doors!=0) { //说明t号门后面还有门 则入队 72 for (int i = 0; i < a[t].doors; ++i) { 73 q.push(a[t].p[i]); 74 75 } 76 } 77 } 78 return t; //因为t 不断被赋值,最后访问的还是t 79 }
首先建立一颗树,其本质是建立一个一维数组;
一开始的第一种方法是建立一维数组,每一个下标存储了每一个结点的数据域以及孩子域,但是因为这道题目不是二叉树,开拓的孩子空间不一样的话会造成浪费;
然后就采用有一个首先知道有多少个孩子,然后通过这个数据来new 一个这么大的空间。
然后就是递归构造、遍历。
9、树的存储结构
1)双亲表示法:需要找父节点的
2)孩子表示法
3)孩子兄弟表示法(左孩子右兄弟)
10、哈夫曼树(最优树 带权路径长度最短的树)
构造:1)根据n个权值构造n棵二叉树
2)选取最小的权值作为左右子树构成一颗新的二叉树,且置新的二叉树的根节点的权值为左右子树的权值之和
3)删除原来的两棵树,并将新的二叉树加入
4)重复操作直到最后只剩一棵树
本章最大收获就是基本懂得如何构造以及遍历一棵树。