一、调试成功程序及说明 1、 题目: 根据二叉树的中序、先序序列创建二叉树,并返回根节点指针; 算法思想: 先根据先序序列找到当前子树的根,然后再在中序序列中寻找这个根,那么左边的就是左分支,右边的就是右分支(当然左不一定是到最左,可能会遇到之前用过的根节点,右边同理)。然后递归构造就行了,毕竟树的定义就是递归的。 运行结果:
结果分析: 结果正确,和老师给的该题二叉树的图像相吻合。 附源程序
BT *createtree(char *in,char *pre,int k) //根据二叉树的中序、先序序列创建二叉树,并返回根节点指针 { BT *T; //子树的根节点 int i; //计算左分支的节点个数 if(k<=0) //没有节点了就说明该分支创建完成 return NULL; else { T = (BT *)malloc(sizeof(BT)); //动态分配空间 T->data = *pre; //子树根节点赋值 for(i=0;in[i]!=*pre;i++); //寻找子树根节点在中序序列的为止,以此来确定左右分支 T->left = createtree(in,pre+1,i); //递归创建左子树 T->right = createtree(in+i+1,pre+i+1,k-i-1); //递归创建右子树 return T; //返回当前的根节点 } }2、 题目: 为每个节点的pa指针赋值(注意根节点的pa指向NULL) 算法思想: 采用后序遍历,因为后序遍历中的栈,满足前一个节点为后一个节点的父亲。这样方便赋值。 运行结果:
结果分析: 运行结果正确。 附源程序
void getpa(BT *T)//为二叉树的每个节点的pa指针赋值(非递归算法) { BT *t[N],*p,*b; //*t[N]为栈,b来帮忙判断右节点是否走过 int top = -1; p = T; do { while(p) { top++; t[top] = p; p = p->left; } b = NULL; while(top>=0) { p = t[top]; if(p->right == b) { top--; p->pa = t[top]; b = p; } else { p = p->right; break; } } }while(top>=0); T->pa = NULL; }3、 题目: 利用递归算法输出根节点到所有叶子节点的路径; 算法思想: 递归寻找到叶子节点(这个判断左右孩子就行了),然后根据父节点向上输出就行了。 存在问题:我用了父节点,因为这里不可以加别的参数或者全局变量,所以我不太会存他的父节点。 运行结果: 结果分析: 运行结果正确,但是由于参数限制,我们不能知道条数 附源程序:
void getpath1(BT *T)//递归算法输出根节点到所有叶子节点的路径 { BT *p; int i; p = T; if(p) { if(!(p->left||p->right)) //判断是不是叶子节点 { printf("Step:"); while((p->pa)!=NULL) //只是为了输出好看,可以除了最后一个,别的都有箭头罢了; { printf("%c<--",p->data); p = p->pa; } printf("%c\n",p->data); } else { getpath1(p->left); //递归选找左分支 getpath1(p->right); //递归选找右分支 } } }4、 题目: 用非递归算法输出根节点到所有叶子节点的路径; 算法思想: 法一:运用后序遍历,因为他的栈里面的元素是满足父子关系的,所以判断栈顶是不是叶子节点,是的话,就把栈里面的元素显示一下; 法二;运用层次遍历,但是需要一个fro[N]来存储它前一个节点的位置。 总上,两种方法都可以找出所有路径,但是层次遍历的路径是从小到大排序的,所以找最长/短路径,还是优先使用层次遍历 运行结果: 结果分析: 结果正确,和3算出来的路径一样 附源程序: 法一:
void getpath2(BT *T)//非递归算法---后序---输出根节点到所有叶子节点的路径 { BT *t[N],*p=T,*b; int step = 0,i; int top = -1; int sum[N]={1},max=1,min=1; do { while(p) { top++; t[top] = p; p = p->left; } b = NULL; while(top>=0) { p = t[top]; if(p->right == b) { if(!(p->left||p->right)) { step++; printf("Step %d: ",step); for(i=0;i<=top-1;i++) { sum[step]++; printf("%c-->",t[i]->data); } printf("%c\n",t[top]->data); } top--; b=p; } else { p = p->right; break; } } }while(top>=0); for(i=1;i<=step;i++) { if(sum[i]<sum[min]) min=i; if(sum[i]>sum[max]) max=i; } printf("最长路径为Step %d,最短路径为Step %d。\n",max,min); }法二:
void getpath3(BT *T)//非递归算法---层次---输出根节点到所有叶子节点的路径 { BT *t[N],*p; int fro[N]; int i,count=0,tmp; int head=0,rear=0; t[rear] = T; fro[rear] = -1; rear++; while(rear> head) { p = t[head]; if(p->left) { fro[rear]=head; t[rear] = p->left; rear++; } if(p->right) { fro[rear]=head; t[rear] = p->right; rear++; } head++; } printf("路线长度从小到大排序为:\n"); for(i=0;i<head;i++) { if(!(t[i]->left||t[i]->right)) { count++; printf("Step %d:%c",count,t[i]->data); tmp = fro[i]; while(tmp>=0) { printf("<--%c",t[tmp]->data); tmp = fro[tmp]; } printf("\n"); } } }5、 题目: 利用递归算法,寻找距离两个指定节点(互相不为对方的祖先)最近的共同祖先,返回该祖先指针; 算法思想: 运用后序遍历,把两个指定节点(互相不为对方的祖先)都存在两个数组里面,然后遍历寻找就行了。 运行结果: 结果分析: 运行正确,显示了他们最近的共同祖先 附源程序:
BT *getcomances(BT *T,char s1,char s2)//寻找距离两个指定节点,即分别包含数据s1和s2的节点(互相不为对 //方的祖先)最近的共同祖先,返回指向该祖先结点的指针 { BT *t[N],*p=T,*b,*num1[N],*num2[N]; int top = -1,i,j,len1=0,len2=0,flag=0; do { while(p) { top++; t[top] = p; p = p->left; } b = NULL; while(top>=0) { p = t[top]; if(p->right == b) { if(p->data == s1) { for(i= top;i>=0;i--) { len1++; num1[top-i] = t[i]; } } if(p->data == s2) { for(i= top;i>=0;i--) { len2++; num2[top-i] = t[i]; } } top--; b=p; } else { p = p->right; break; } } }while(top>=0); for(i=0;i<len1;i++) { for(j=0;j<len2;j++) { if(num1[i]->data == num2[j]->data) { flag = 1; break; } } if(flag==1) break; } return num1[i]; }6、 题目: 按层次逐行输出二叉树的节点数据(每一层单独显示一行),并判断该二叉树是否为完全二叉树 算法思想: 1.设置一个height[N]数组,存储每一个节点的层数,用层数变化判断,是否需要换行。 2.设置了一个权重,左孩子的权重为2,右孩子的权重为1,那么我们可以记录下每一个节点的权重,当权重num[N]中大的在前,小的在后,2的个数不超过1个,1的个数为0个。 运行结果: 本题样例
只有根
树为空
结果分析: 运行结果正确,我用了一些特殊的数来测试 附源程序:
//我设置了一个权重,左孩子的权重为2,右孩子的权重为1,那么我们可以记录下每一个节点的权重,当权重num[N]中大的在前,小的在后,2的个数不超过1个 int layervisit(BT *T)//层次遍历二叉树,逐行输出节点数据(每一层单独一行显示),并判断该二叉树 //是否为完全二叉树,如果是,返回1,否则返回0 { BT *t[N],*p; //队列 int head=0,rear=0,height[N],num[N]={0},flag=1,count=0,i; // height记录节点的层数 if(!T) return 1; t[rear] = T; height[rear] = 1; rear++; while(rear>head) { p = t[head]; printf("%c ",p->data); if(p->left) { t[rear] = p->left; height[rear] = height[head]+1; num[head] = num[head]+2; rear++; } if(p->right) { t[rear] = p->right; height[rear] = height[head]+1; num[head] = num[head]+1; rear++; } if(height[head]!=height[head+1]) printf("\n"); head++; } for(i=1;i<head;i++) { if(num[i-1]<num[i]) //判断前后两个的大小关系 { flag = 0; break; } if(num[i]==1||num[0]==1) //判断有没有点只有右孩子 { flag = 0; break; } if(num[i]==2) //判断只有左孩子点的个数 count++; } if(num[0]==2) //上面从i=1开始循环,所以要额外判断i=0 count++; if(count>1) //如果个数超过1个,那么就不是完全二叉树 flag = 0; return flag; }7、 题目: 销毁以指定节点为根节点的子树 算法思想: 采用层次遍历,用了一个数组fro[N]来存储当前节点的父节点(上一个节点),用数组child[N]来存储他是左孩子还是右孩子,然后在入队出队的过程中,寻找该节点,找到后,让该节点的前一个节点(父节点)相应的孩子节点指向空,然后调用delbt(BT *T)函数将其删除。 运行结果:
结果分析: 结果正确,成功删除该子树。 附源程序:
BT *delsubtree(BT *T,char s)//找到包含数据为s的节点,并删除以该节点为根的子树,最后返回根节点 //(因为这个删除也可能会删除整个树,所以结束后返回当前二叉树的根节点指向) { BT *t[N],*p,*fro[N]; int child[N]; int head=0,rear=0; t[rear] = T; child[rear] = -1; fro[rear] = NULL; rear++; while(rear> head) { p = t[head]; if(p->data == s) break; if(p->left) { fro[rear] = p; child[rear] = 0; //0表示左孩子 t[rear] = p->left; rear++; } if(p->right) { fro[rear] = p; child[rear] = 1; //1表示右孩子 t[rear] = p->right; rear++; } head++; } if(child[head] == 0) fro[head]->left = NULL; else if(child[head] == 1) fro[head]->right = NULL; p = delbt(p); return T; } BT *delbt(BT *T) { if(T) { T->left = delbt(T->left); T->right = delbt(T->right); free(T); } return NULL; }