首先上代码后面对代码经行分析。
代码
#include <stdio.h>
#include <stdlib.h>
typedef struct tree
{
char date;
struct tree *left_leaf;
struct tree *right_leaf;
}tree,*BiTree;
void creat_tree(BiTree *T)//创建树
{
char val;
scanf("%c",&val);
//getchar();
if(val=='#')
{
*T=NULL;
}
else
{
*T=(BiTree)malloc(sizeof(tree));
(*T)->date=val;
creat_tree(&(*T)->left_leaf);
creat_tree(&(*T)->right_leaf);
}
}
void preorder_traversal(BiTree T)//先序遍历
{
if(T==NULL)
{
return;
}
printf("%c ",T->date);
preorder_traversal(T->left_leaf);
preorder_traversal(T->right_leaf);
}
void inorder_traversal(BiTree T)//中序遍历
{
if(T==NULL)
{
return;
}
inorder_traversal(T->left_leaf);
printf("%c ",T->date);
inorder_traversal(T->right_leaf);
}
void epilogue_traversal(BiTree T)
{
if(T==NULL)
{
return;
}
epilogue_traversal(T->left_leaf);
epilogue_traversal(T->right_leaf);
printf("%c ",T->date);
}
int main(void)
{
BiTree T;
T=(BiTree)malloc(sizeof(tree));
printf("请输入先序排列的二叉树,空节点为#\n");
creat_tree(&T);
printf("先序输出\n");
preorder_traversal(T);
printf("\n");
printf("中序输出\n");
inorder_traversal(T);
printf("\n");
printf("后序输出\n");
epilogue_traversal(T);
printf("\n");
}
运行结果:
可以看到程序运行没有问题的。下面我们经行代码刨析!
代码解析
先简单说一下递归思想
比如有这样一个函数。
void fun(int x)
{
x++;--------------s
printf("%d \n",x);
if(x==10)
{
return;
}
fun(x);-----------p
printf("i= %d\n",i++);
}
假设x初始值为0,当程序第一次运行到p的时候,此时x=1,然后递归再从s处运行,跑到p处后再次递归,又从s处接着运行。一直到x等于10时候遇到return停止递归。虽然遇到return但是这时候程序并没有停止,因为最后一次fun函数运行,上一次的fun函数还没有运行结束,因为printf还没有运行过。一个函数没有结束是不可以被调用的。上一次函数运行还需要上上次函数运行结束。说起来可能有点抽象画个图来说明吧!
1函数函数体:
void 1 (void)
{
2();
printf("1\n");
}
2函数函数体:
void 2 (void)
{
3();
printf("2\n");
}
3函数函数体:
void 3 (void)
{
4();
printf("3\n");
}
4函数函数体:
void 4 (void)
{
return;
}
如果我调用1函数,输出为:3-2-1。
1函数执行需要依赖2函数运行完,2函数运行完需要3函数,3函数运行完需要4函数。只有4函数执行完成3函数才能执行完,3函数执行完2函数才能执行完,2函数执行完1函数才能结束。所以输出为3-2-1.
这就是递归。下面我们会接着分析二叉树。
先序遍历
二叉树图如图。
先序遍历顺序为,根左右。我们看一下先序遍历的代码。
void preorder_traversal(BiTree T)//先序遍历
{
if(T==NULL)
{
return;
}
printf("%c ",T->date);
preorder_traversal(T->left_leaf);----------1
preorder_traversal(T->right_leaf);---------2
}
先序遍历结果是
A B D G # # H # # # C E # I # # F # #
首先程序运行到1位置时候,开始递归。
一直遍历左子树到G,然后再左为空进入return,此时1函数最内层的递归已近结束了。然后开始回溯执行D节点的未执行的2函数,D节点的右子树为H不为空,运行完printf又遇到1函数,然后H节点左右子树都是空,H节点处于的函数执行完成然后D节点处于的函数才能执行完成,接着回溯到B节点的2函数,B节点右子树为空,B节点处于的函数运行结束,到A函数,然后A函数执行2函数,到C函数。到现在A节点的左子树遍历完成。开始右边遍历。到C节点后,C节点运行到1函数时候,有子节点E,节点E左子树为空右子树为I然后I左右子树为空.这里可能有点绕了,我们给E节点单独拿出来。
if(T==NULL)
return;
printf("1%c ",T->data);---------------a
PreOrderTravel(T->left);-------------1
printf("\n");
printf("2%c ",T->data);--------------b
PreOrderTravel(T->right);--------------2
当遍历到E节点时候,运行到a函数,打出E节点的数值1E,然后再执行1函数,E左子树为空,再次执行的1函数运行结束,但是E节点在再次执行1函数时候下面函数没有运行,在1函数截止后,E节点1函数下面开始运行,所以打出了2E,然后开始2函数,遍历到I节点,再printf打印出I节点数值后,再次遇到1函数,调用后左子树为空。结束,此时1函数运行结束,接着下面运行b函数打印出2I。然后在调用2函数,右子树为空函数结束。这时候刚遍历到E节点才完全执行完成,C节点的整个左子树遍历完成,然后C节点那个阶段的函数执行到b函数,打印出2C,接着执行2函数,C的右子树为F不为空,打印出1F,执行的2函数执行到1函数时候,左子树为空。调用的1函数执行完成。然后接着运行到b函数,打印出2F,然后运行2函数。F右子树为空。整个遍历结束。
递归确实很难,但是真的很迷人。
上面将先序遍历将完了,然后创建二叉树的过程分析不就简单了嘛!虽然确实很难理解比较抽象,但是抽丝剥茧慢慢来还是可以理解的。
老脸一红(我也是看了一天才整明白的)。
总结一下递归的注意事项:
A函数调用B函数,B函数调用C函数,A函数要想调用B函数,B函数必须运行完成,B函数运行完成需要C函数运行完成。也就是说要想A函数运行完成,必须C函数执行完成,然后让 B函数执行完成,最后A函数才能执行完成。
粗言寡语,表达有限。见谅。
如有错误欢迎指正。thanks^ _ ^