数据结构:第五章学习小结
第五章我们主要学习了树和二叉树的定义、性质、存储结构以及部分操作还有哈夫曼树。
下图是我对本章所学知识的大致总结:
在这章的代码题中,我也学到了很多,其中List leaves这题就有很多小细节:
1.
bool check[n] = {false};//定义bool类型的数组来查找未出现过的结点
2.
2.void LevelOrder(Tree T) { queue<int> Q; int k; Q.push(T.root); bool flag = false; while(!Q.empty()) { k = Q.front(); Q.pop(); if(T.data[k].lch==-1&&T.data[k].rch==-1) { if(!flag) { cout << k; flag = true; } else cout << " " << k; } else { if(T.data[k].lch!=-1) Q.push(T.data[k].lch); if(T.data[k].rch!=-1) Q.push(T.data[k].rch); } } }
该层次遍历函数用队列来不停存放和弹出结点,并在这个过程中输出所有叶子结点的编号。
树的同构对我来说难度很大,因为递归的方式去思考问题很容易把我绕晕,好在发现了这篇博客:https://blog.csdn.net/wanmeiwushang/article/details/52740354
作者的思路如下:我们考虑用静态链表的方式也就是数组存储树简单一点,因为此题节点与节点之间是有下标关系的,输入的顺序就是节点的编号,某个节点的保存了左右子树的下标,所以可以通过下标访问数组的方式得到它的左右子树。还有一个需要解决的问题就是我们判断两棵树是否同构,首先要找到它的根节点才能进行下面的比较,从输入的数据以及图中可以发现根节点是没有节点指向它的,所以我们用check数组保存所有节点的指向(下标)置为0,每次从输入中获取到指向后,将指向的数组位置置为1,最后没有被指到的位置的节点即为根节点。
作者代码如下:
#include<stdio.h> #define MaxTree 10 #define Null -1 //将Null定义为-1而不能是0,因为数组下标为0的地方仍保存有节点 typedef char ElementType; typedef int Tree; struct TreeNode{ //定义二叉树节点 ElementType Data; Tree Left; Tree Right; }T1[MaxTree],T2[MaxTree]; int N,check[MaxTree]; //check数组用于寻找树的根节点 Tree BuildTree(struct TreeNode T[]){ int Root=Null,i; //刚开始将节点置为空,若为空树的时候可返回Null char cl,cr; scanf("%d",&N); if(N){ //如果不为空树的话 for(i=0;i<N;i++) check[i]=0; //将check数组置为0 for(i=0;i<N;i++){ scanf("\n%c %c %c",&T[i].Data,&cl,&cr); //把换行符放在前面吃掉前面scanf后的回车,而最后一个scanf不能有回车,一举两得 if(cl!=‘-‘){ T[i].Left=cl-‘0‘; check[T[i].Left]=1; } else T[i].Left=Null; if(cr!=‘-‘){ T[i].Right=cr-‘0‘; check[T[i].Right]=1; } else T[i].Right=Null; } for(i=0;i<N;i++) if(!check[i]) break; Root=i; } return Root; } int Isomorphic(Tree R1,Tree R2){ if((R1==Null)&&(R2==Null)) //如果为空树则是同构的 return 1; if(((R1==Null)&&(R2!=Null))||((R1!=Null)&&(R2==Null)))//如果一个为空一个不为空则不是同构的 return 0; if((T1[R1].Data)!=(T2[R2].Data))//如果数据不同则不是同构的 return 0; //如果左儿子都为空判断右儿子是否同构:主要看以上三个方面(1)右儿子是否都为空(2)是否一个有右儿子一个没有(3)右儿子数据是否相同 if((T1[R1].Left==Null)&&(T2[R2].Left==Null)) return Isomorphic(T1[R1].Right,T2[R2].Right); /* 如果两棵树左儿子都不为空并且数据还是一样的,对左儿子进行递归*/ if ( ((T1[R1].Left!=Null)&&(T2[R2].Left!=Null))&&((T1[T1[R1].Left].Data)==(T2[T2[R2].Left].Data)) ) return ( Isomorphic( T1[R1].Left, T2[R2].Left )&&Isomorphic( T1[R1].Right, T2[R2].Right ) ); /* 如果两棵树左儿子(一个空一个不空或者都不空)并且数据不一样,那么判断第一棵树的左(右)儿子是否跟第二棵树的右(左)儿子同构 */ else return ( Isomorphic( T1[R1].Left, T2[R2].Right)&&Isomorphic( T1[R1].Right, T2[R2].Left ) ); } int main(){ Tree R1,R2; //首先建立两棵树,R1,R2为树的根节点 R1=BuildTree(T1); R2=BuildTree(T2); if(Isomorphic(R1,R2)) //Isomorphic函数判断是否同构 printf("Yes\n"); else printf("No\n"); return 0; }
学习心得:递归的一些算法虽然较难理解,但是用好了也大大得缩短了代码长度,所以还是非常重要的,我希望在接下来的训练中可以在打代码的过程中逐步加深对递归算法的掌握,同时也希望自己能够做题再严谨一些,顾及一个问题的方方面面。