第五章小结
本章学的是二叉树以及其具体操作。
我们学习了二叉树的几个性质,说实话好像数学公式一样。性质如下
1)在二叉树的第i层上至多有2i-1个结点。
2)深度为k的二叉树上至多含2k - 1个结点(k≥1)。
3)对任何一棵二叉树,若它含有n0个叶子结点、n2个度为2的结点,则必存在关系式:n0 = n2 + 1。
4)具有n个结点的完全二叉树的深度为 (log2n) +1 。
5)对完全二叉树,若从上至下、从左至右编号,则编号为 i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2。
之后学了哈夫曼树,感觉上课时学的很带劲,但是课后左右还是挺吃力的。
上课的时候觉得树的结构不算太难,毕竟有规律可循。但是真正用起来的时候发现并没那么简单。拿下面这两题为例吧
首先是实践的第二题,深入虎穴。代码如下。
#include<iostream> #include<queue> using namespace std; typedef struct{ int doors;//代表门的数量 int *p; //p指向具体门的编号 ,把p看作整型数组 }node; int input(node *&a) { int n,m,i,k,j; bool *vi; cin>>n; a=new node[n+1];//为a数组申请空间 vi=new bool[n+1];//同上 for(i=1;i<=n;i++)//将vi数组初始化 { vi[i]=false; } for(i=1;i<=n;++i) { cin>>a[i].doors; a[i].p=new int[a[i].doors]; for(k=0;k<a[i].doors;++k)//new的空间下标为0到n-1 { cin>>a[i].p[k]; vi[a[i].p[k]]=true; } } for(j=1;j<=n;j++) { if(!vi[j]) break; }return j; } int find(node*a,int root) {//从a数组root下标往下搜索 queue<int> q;//定义用于存放待访问的门编号的队列 int x; q.push(root);//根编号入队 while(!q.empty())//队列非空 { x=q.front(); q.pop(); for(int i=0;i<a[x].doors;++i) { q.push(a[x].p[i]); } } return x; } int main() { node *a;//定义一个动态的整型数组 int root; root=input(a);//找根 cout<<find(a,root)<<endl;//从根开始寻找 //cout<<root; return 0; }
这题是老师上课时带着我们一起做的。回来自己试的时候发现几个难点。首先第一个时动态申请的范围。申请是从第0个开始,所以有的循环要从i=0开始,不然会出错输不出答案。然后是find函数中使用队列,学到了用queue的头文件的一些使用方法,以及层次遍历的这种思想。
印象更深刻的是下面这道叶子节点的查找的题目。
代码如下
#include<iostream> #include<queue> using namespace std; typedef struct { char lchild;//左孩子的下标 char rchild;//右孩子的下标 }node; void levelOrderTraverse(node a[],int x); int createBiTree(node a[]); int main() { node a[10]; int x; x=createBiTree(a);//带回根节点 levelOrderTraverse(a,x); } int createBiTree(node a[]) { int n, i; char x,y; bool t[10]={false}; //用来查找根节点 cin>>n; for(i=0;i<n;i++) { cin>>x>>y; if(x!='-')//左节点 { a[i].lchild=x-'0';//-'0'的作用是减去0的ASCII值,用于转换 t[a[i].lchild]=true; } else a[i].lchild=-1;//无孩子 if(y!='-')//右节点处 { a[i].rchild=y-'0';//同上 t[a[i].rchild]=true;// } else a[i].rchild=-1;//无孩子 } for(i=0;i<n;i++) {//找根节点 if(!t[i]) return i; } } void levelOrderTraverse(node a[],int x) {//层次遍历 queue<int> q; int m; int leaves=0;//标记是第几个叶子结点 q.push(x);//入队 while(!q.empty()) { m=q.front();//取队头 q.pop(); if(a[m].lchild==-1&&a[m].rchild==-1) { if(leaves) cout<<" "; cout<<m; ++leaves; } if(a[m].lchild!=-1) q.push(a[m].lchild);//左孩子入队 if(a[m].rchild!=-1) q.push(a[m].rchild); //右孩子入队 } }
这道题一开始没什么想法。特别时自上而下自左到右的输出方式,是老师讲了层次遍历后才有了点想法。这道题中,请教了一些同学,发现了一个转换字符数字的方法就是
a[i].lchild=x-'0'
这里的-‘0’实际上是减去了0的ASCII码,这样很容易的实现了转换。这题感觉和上一题很想,不过这题是自己敲出来的,感觉很不一样。特别是用上了新学到的队列处理,成就感还是很大的。
下一章的学习希望自己能跟紧老师的步伐吧,也要时刻提醒自己之前学习的知识可以用,就比如这次的队列一样。