在存储树结构时我们一般是用左孩子右兄弟法将一棵树转化为二叉树,利用二叉链表进行表示。
然而当树的规模较小时,我们也可以考虑用静态链表来实现,这样的好处是可以方便地建树,同时在处理某些简单地问题时用数组也会更容易上手。
1 struct node{ 2 DataType data; 3 int LChild,RChild;//存储左右孩子节点的下标,无则置为-1 4 }Tree[maxn];
两道用这种存储结构来写的例题(均来源于PTA平台):
第一道题是判断给定的两棵树是否同构(给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。)
这里把两棵树分别储存之后用了一种类似宽搜的比较方法,感觉这种方法在这种简单的问题有时还挺好用的:根节点先入队,处理完当前结点后就将其左右孩子入队,根节点出队。这种方式保证了可以按照层次遍历的顺序去扫描整棵树。
在这道题中需要做的处理是比较队头节点的左右孩子是否匹配(左左+右右 或者 左右+右左),注意这会影响到孩子结点入队的顺序
写的好丑T_T
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #define maxn 30 5 struct node{ 6 char data; 7 int LChild,RChild; 8 }T1[maxn],T2[maxn]; 9 void GetTree(struct node T[maxn],int tot,int *root){//读入,建树 10 bool vis[20]; 11 memset(vis,0,sizeof(vis)); 12 char num,l,r; 13 int i; 14 for (i=0;i<tot;i++){ 15 scanf("%c %c %c",&num,&l,&r); 16 T[i].data=num; 17 T[i].LChild=l==‘-‘?-1:l-‘0‘; 18 T[i].RChild=r==‘-‘?-1:r-‘0‘; 19 if (l!=‘-‘) vis[T[i].LChild]=1; 20 if (r!=‘-‘) vis[T[i].RChild]=1; 21 getchar(); 22 } 23 for (i=0;i<tot;i++)//找根节点 24 if (!vis[i]){ 25 *root=i;break; 26 } 27 } 28 bool Init(int *n,int *root1,int *root2){ 29 int tot1,tot2,r1,r2; 30 scanf("%d\n",&tot1); 31 GetTree(T1,tot1,&r1); 32 scanf("%d\n",&tot2); 33 if (tot1!=tot2) return 0;//结点个数不同直接跳出 34 else *n=tot1; 35 GetTree(T2,tot2,&r2); 36 *root1=r1;*root2=r2; 37 return 1; 38 } 39 void Solve(int n,int root1,int root2){ 40 if (!n) {//坑点:两棵空树同构 41 puts("Yes"); 42 return; 43 } 44 if (T1[root1].data!=T2[root2].data){ 45 puts("No"); 46 return; 47 } 48 int q1[maxn],q2[maxn]; 49 int front=0,rear=1; 50 q1[front]=root1; 51 q2[front]=root2; 52 while (front<rear){ 53 bool flag;//标记左右用不用换位置 54 //比较当前结点左右孩子 55 if (((T1[T1[q1[front]].LChild].data==T2[T2[q2[front]].LChild].data)&& 56 (T1[T1[q1[front]].RChild].data==T2[T2[q2[front]].RChild].data))) 57 flag=0; 58 else if((T1[T1[q1[front]].LChild].data==T2[T2[q2[front]].RChild].data)&& 59 (T1[T1[q1[front]].RChild].data==T2[T2[q2[front]].LChild].data)) 60 flag=1; 61 else { 62 puts("No"); return; 63 } 64 //左右孩子入队 65 int left1=T1[q1[front]].LChild,left2=T2[q2[front]].LChild; 66 int right1=T1[q1[front]].RChild,right2=T2[q2[front]].RChild; 67 if (left1!=-1){ 68 q1[rear]=left1; 69 q2[rear++]=flag?right2:left2; 70 } 71 if (right1!=-1){ 72 q1[rear]=right1; 73 q2[rear++]=flag?left2:right2; 74 } 75 front++; 76 } 77 puts("Yes");//全部比较完没有报错就正确 78 } 79 int main(){ 80 int n,root1,root2; 81 if (Init(&n,&root1,&root2)) Solve(n,root1,root2); 82 else puts("No"); 83 return 0; 84 }
上面链接里的另外两个算法总结,也都是利用静态链表存储实现的:
一个是利用所有数据不重复的特点,直接双重循环比较每个同数据节点的左右孩子一不一样
另一个是常规的解法,递归比较
第二道题是以一样的格式输入,按层序遍历顺序输出所有叶子结点
1 void search(int n,int m,int root){//n为结点个数,m为已知叶子结点数,root为根 2 int queue[15]; 3 int front=0,rear=1; 4 queue[front]=root; 5 while (front<rear){ 6 if (Tree[queue[front]].left==-1&&Tree[queue[front]].right==-1){ 7 m--; 8 printf("%d",Tree[queue[front]].num); 9 if (m) printf("%s"," "); 10 else break; 11 } 12 if (Tree[queue[front]].left!=-1) queue[rear++]=Tree[queue[front]].left; 13 if (Tree[queue[front]].right!=-1) queue[rear++]=Tree[queue[front]].right; 14 front++; 15 } 16 }
原文:https://www.cnblogs.com/hwh-since2019/p/12723369.html