一、调试成功程序及说明
1、
题目:
1、利用递归算法输出根节点到所有叶子节点的路径;
算法思想:
用一个数组path[N]来存储他的祖先,即根节点到该节点的路径,然后调用allpath()函数,判断该节点是不是叶子节点,是的话就输出,否则继续向下(孩子)走。
运行结果:
结果分析:
运行正确。
附源程序
void allpath(TR *T,char *path,int m)
{
TR *p;
int i;
if(T)
{
if(!(T->fir)) //该节点为叶子,那么就输出当前的路径,因为path里面存的都是他的祖先
{
path[m] = T->data;
printf("Step :");
for(i=0;i<m;i++)
printf("%c-->",path[i]);
printf("%c\n",path[m]); //为了输出好看,所以把最后一个拿出来了
}
else //开始寻找以T为根节点的子树的所有路径
{
path[m]=T->data;
m++;
p = T->fir; //先找第一个孩子
while(p)
{
allpath(p,path,m);
p = p->sib; //然后寻找他的所有兄弟
}
}
}
}
void getpath1(TR *T)//递归算法输出根节点到所有叶子节点的路径
{
int m=0;
char path[N];
allpath(T,path,m);
}
2、
题目:
2、利用非递归算法输出根节点到所有叶子节点的路径;
算法思想:
法一:
运用后序遍历(即孩子—兄弟树的中序遍历),判断当前栈顶元素是否为叶子,是的话,就输出栈的所有元素(不是弹栈,只弹栈顶)
法二:
运用层次遍历,加上一个front[N]来记录父亲节点,然后对队列遍历一次即可,判断里面的元素是不是叶子节点,是的话,就按照front[N]来输出路径,这样做的好处在于,可以很方便的找出最长/最短的路径。(输出的路径的长度也是从小到大的)
运行结果:
结果分析:
运行正确。
附源程序:
法一:
void getpath2(TR *T)//非递归算法---后序---输出根节点到所有叶子节点的路径
{
TR *t[N],*p;
int top = -1,count=0,i;
p = T;
while(top>=0||p)
{
while(p)
{
top++;
t[top] = p;
p = p->fir;
}
p = t[top];
top--;
if(p->fir==NULL)
{
count++;
printf("Step %d:",count);
for(i=0;i<=top;i++)
printf("%c-->",t[i]->data);
printf("%c\n",p->data);
}
p = p->sib;
}
}
法二:
void getpath3(TR *T)//非递归算法---层次---输出根节点到所有叶子节点的路径
{
TR *t[N],*p;
int front[N],count=0,i,tmp;
int head=0,rear=0;
t[rear] = T;
front[rear] = -1;
rear++;
while(rear>head)
{
p = t[head];
p = p->fir;
while(p)
{
t[rear] = p;
front[rear] = head;
rear++;
p = p->sib;
}
head++;
}
printf("路线长度从小到大排序为:\n");
for(i=0;i<rear;i++)
{
if(!(t[i]->fir))
{
count++;
printf("Step %d:%c",count,t[i]->data);
tmp = front[i];
while(tmp>=0)
{
printf("<--%c",t[tmp]->data);
tmp = front[tmp];
}
printf("\n");
}
}
}
3、
题目:
3、利用递归算法统计数中叶子节点的数量;
算法思想:
判断当前节点是不是叶子节点,是的话就返回1,否则继续选找以该节点为根节点的子树(可以递归了),找出他所有的叶子节点的个数,并且用m来存储。
运行结果:
结果分析:
运行正确
附源程序:
int leaftree(TR *T)//递归计算并返回树的叶子节点的数量
{
TR *p;
int m=0;
if(T) //T为真进入
{
if(!(T->fir)) //找到叶子
return 1; //返回1,让最终的m加1
else //否则找完以T为根节点的分支
{
p = T->fir; //p赋值为T的第一个孩子,开始寻找T的第一个孩子为根节点的叶子数量
while(p)
{
m = m+leaftree(p);
p = p->sib; //寻找T的其他孩子(即T的第一个孩子的兄弟)为根节点的叶子数量
} //结束,说明以T为根节点的子树已经找完。
}
}
return m; //返回最后该(子)树的所有叶子。
}
4、
题目:
计算树的宽度(包含节点数最多的那一层节点数量);
算法思想:
我们用一个数组height记录高度,即当前节点的所在高度,width记录每一层的节点个数,然后因为height是单调有界的,只需要一次遍历,对相应的width进行++操作就行了(width[height[i]]++),这个灵感来自于原来的一道题;数字范围为1-100000的正数。这样可以有效的降低时间复杂度。
运行结果:
结果分析:
运行正确
附源程序
int weidth(TR *T)//输出树的宽度(包含节点数量最多的那一层的节点数量即为树的宽度)
{
TR *t[N],*p;
int head=0,rear=0,i,max; //max记录点数量最多的那一层的节点数量
int height[N],width[N]={0}; //height记录高度,即当前节点的所在高度,width记录每一层的节点个数
height[rear] = 1;
t[rear] = T;
rear++; //入队
while(rear>head) //层次遍历
{
p = t[head];
p = p->fir;
while(p)
{
height[rear] = height[head]+1; //记录该节点的高度
t[rear] = p;
rear++;
p = p->sib;
}
head++;
}
for(i=0;i<rear;i++) //记录每一层的节点个数
width[height[i]]++;
max = width[1]; //寻找最大的
for(i = 2;i<height[rear-1];i++) //注意到height里面的元素的单调性,就可以轻松判断count的元素个数
if(width[i]>max)
max = width[i];
return max;
}
5、
题目:
在指定位置插入一个节点(选做);
算法思想:
首先先运用后序遍历找到插入点的位置(这里运用层次也可以,那么和上面的选找路径一样,再加一个front[N]来存储父节点的位置),然后对i分类讨论,0或者>0,i=0的话,就插入在p的fir,和p->fir->sip之间,否则就插在p1和p1->sip之间,这里的p1只需要一次简单的遍历就可以找到了。但是要记住,最后插入节点S后,S->fir要赋值为NULL。
运行结果:
结果分析:
运行正确
附源程序
void insdata(TR *T,char subroot,int i,char data)
{
TR *t[N],*p=T,*p1,*p2,*S; //p为插入的位置的父节点subroot,p1为插入的位置的上一个兄弟,p2为插入的位置的下一个兄弟,S为插入的节点
int top = -1;
int flag = 0; //判断插入位置是否存在
if(T)
{
while(top>=0||p) //后序遍历,选找插入的位置
{
while(p)
{
top++;
t[top] = p;
p = p->fir;
}
p = t[top];
top--;
if(p->data==subroot)
{
flag = 1;
break;
}
p = p->sib;
}
if(flag==1) //说明插入位置存在
{
S = (TR *)malloc(sizeof(TR)); //动态分配空间
S->data = data;
p1=p;
p2=p1->fir;
if(i==0) //分情况,是第一孩子,还是兄弟,这里的代码不一样的,一个是fir,一个是sip,无法代码合并
{
p->fir = S;
S->sib = p2;
}
else
{
while(i>0) //找是插在第几个分支的后面
{
i--;
p1 = p2;
p2 = p2->sib;
if(p2==NULL) //如果i大于该结点的分支总数,则就在该结点的最后一个分支的后边做插入
break;
}
p1->sib = S;
S->sib = p2;
}
S->fir = NULL; //这个不能丢,要给S的第一个孩子赋值为NULL
}
}
}
6、
题目:
销毁以指定节点为根节点的子树(选做);算法思想:
运行结果:
结果分析:
运行正确
附源程序
TR *delsubtree(TR *T,char s)//销毁以包含数据s为根节点的子树,并返回删除后树的根节点指向
{
TR *t[N],*p,*p1,*p2;
int top = -1;
p = T;
while(top>=0||p)
{
while(p)
{
top++;
t[top] = p;
p = p->fir;
}
p = t[top];
top--;
if(p->data==s)
break;
p = p->sib;
}
p1 = t[top];
p2 = p1->fir;
if(p2==p)
t[top]->fir=p->sib;
else
{
while(p2)
{
if(p2==p)
break;
p1 = p2;
p2 = p2->sib;
}
p1->sib = p->sib;
}
deltree(p);
return T;
}
二、小结
大概1个多小时写完了,因为之前练习了老师上课的代码tree.c很多次,除了第五个忘了写S->fir=NULL外,别的都是一遍过(写完直接编译,运行正确),感觉这次题目很简单,但是我不会掉以轻心,后面还会继续练习(因为今天练习迷宫的时候,我发现我只记得用栈和队列来写了,对于递归的有点忘了)。
很多操作都用到了后序/层次遍历,感觉这两种遍历方式很常用。以及感觉有一个模板,就是,递归的,都有这个样子的代码:
p = T->fir; //有时更改为p->fir->sib
While(p)
{
递归函数(p,...);
P = p->sib;
}
感觉有点像套路。