
二叉树递归与非递归实现代码:
一、三种遍历方式的递归实现(比较简单,这里不详细讲解
1、先序遍历——按照“根节点-左孩子-右孩子”的顺序进行访问。
2、中序遍历——按照“左孩子-根节点-右孩子”的顺序进行访问。
void in_traverse(BTree pTree)
{
if(pTree)
{
if(pTree->pLchild)
in_traverse(pTree->pLchild);
printf("%c ",pTree->data);
if(pTree->pRchild)
in_traverse(pTree->pRchild);
}
}
3、后序遍历——按照“左孩子-右孩子-根节点”的顺序进行访问
void beh_traverse(BTree pTree)
{
if(pTree)
{
if(pTree->pLchild)
beh_traverse(pTree->pLchild);
if(pTree->pRchild)
beh_traverse(pTree->pRchild);
printf("%c ",pTree->data);
}
二,三种遍历方式的非递归实现
为了便于理解,这里以下图的二叉树为例,分析二叉树的三种遍历方式的实现过程。

1、前序遍历的非递归实现
根据先序遍历的顺序,先访问根节点,再访问左子树,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的先序遍历顺序为:ABDECF。非递归的实现思路如下:
对于任一节点P,
1)输出节点P,然后将其入栈,再看P的左孩子是否为空;
2)若P的左孩子不为空,则置P的左孩子为当前节点,重复1)的操作;
3)若P的左孩子为空,则将栈顶节点出栈,但不输出,并将出栈节点的右孩子置为当前节点,看其是否为空;
4)若不为空,则循环至1)操作;

这里是这样的,先压入根节点,然后判断左子树是否为空,不为空就压入栈,否则退出while循环之后就将null结点出栈,再判断当前栈是否为空,如果非空就出栈得到父节点然后判断右孩子,压入右孩子结点,再判断此右子树的左孩子是否为空,继续循环。当初在面试的时候因为就给了几十秒的时间有点紧张,所以想出了用数组存储每次遍历后的节点的父节点,但其实二叉树的前序遍历有个特点就是遍历到左叶子节点的时候需要回头再去遍历之前未遍历到的右节点,这个特点刚好和栈的先进后出的特点很相符,所以应该改用栈来存储未遍历到的右节点。b:弄个栈,配对结束后如果栈空,输出原字符串,否则输出栈的内容,如果栈中有')',左边补'(',如果栈中有'(',右边补')'.输出即可.。
6)直到当前节点P为NULL并且栈空,遍历结束。
下面以上图为例详细分析其先序遍历的非递归实现过程:
首先,从根节点A开始,根据操作1),输出A,并将其入栈,由于A的左孩子不为空,根据操作2),将B置为当前节点,再根据操作1),将B输出,并将其入栈,由于B的左孩子也不为空,根据操作2),将D置为当前节点,再根据操作1),输出D,并将其入栈,此时输出序列为ABD;
由于D的左孩子为空,根据操作3),将栈顶节点D出栈,但不输出,并将其右孩子置为当前节点;
由于D的右孩子为空,根据操作5),继续将栈顶节点B出栈,但不输出,并将其右孩子置为当前节点;
由于B的右孩子E不为空,根据操作1),输出E,并将其入栈,此时输出序列为:ABDE;
由于E的左孩子为空,根据操作3),将栈顶节点E出栈,但不输出,并将其右孩子置为当前节点;
由于E的右孩子为空,根据操作5),继续将栈顶节点A出栈,但不输出,并将其右孩子置为当前节点;
由于A的右孩子C不为空,根据操作1),输出C,并将其入栈,此时输出序列为:ABDEC;
由于A的左孩子F不为空,根据操作2),则将F置为当前节点,再根据操作1),输出F,并将其入栈,此时输出序列为:ABDECF;
由于F的左孩子为空,根据操作3),将栈顶节点F出栈,但不输出,并将其右孩子置为当前节点;
由于F的右孩子为空,根据操作5),继续将栈顶元素C出栈,但不输出,并将其右孩子置为当前节点;
此时栈空,且C的右孩子为NULL,因此遍历结束。
void pre_traverse(BTree pTree)
{
PSTACK stack = create_stack(); //创建一个空栈
BTree node_pop; //用来保存出栈节点
BTree pCur = pTree; //定义用来指向当前访问的节点的指针
//直到当前节点pCur为NULL且栈空时,循环结束
while(pCur || !is_empty(stack))
{
//从根节点开始,输出当前节点,并将其入栈,
//同时置其左孩子为当前节点,直至其没有左孩子,及当前节点为NULL
printf("%c ", pCur->data);
push_stack(stack,pCur);
pCur = pCur->pLchild;
//如果当前节点pCur为NULL且栈不空,则将栈顶节点出栈,
//同时置其右孩子为当前节点,循环判断,直至pCur不为空
while(!pCur && !is_empty(stack))
{
pCur = getTop(stack);
pop_stack(stack,&node_pop);
pCur = pCur->pRchild;
}
}
}
2、中序遍历的非递归实现
根据中序遍历的顺序,先访问左子树,再访问根节点,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的中序遍历顺序为:DBEAFC。非递归的实现思路如下:

对于任一节点P,
1)若P的左孩子不为空,则将P入栈并将P的左孩子置为当前节点,然后再对当前节点进行相同的处理;
2)若P的左孩子为空,则输出P节点,而后将P的右孩子置为当前节点,看其是否为空;
3)若不为空,则重复1)和2)的操作;
4)若为空,则执行出栈操作,输出栈顶节点,并将出栈的节点的右孩子置为当前节点,看起是否为空,重复3)和4)的操作;
5)直到当前节点P为NULL并且栈为空,则遍历结束。
下面以上图为例详细分析其中序遍历的非递归实现过程:
首先,从根节点A开始,A的左孩子不为空,根据操作1)将A入栈,接着将B置为当前节点,B的左孩子也不为空,根据操作1),将B也入栈,接着将D置为当前节点,由于D的左子树为空,根据操作2),输出D;
由于D的右孩子也为空,根据操作4),执行出栈操作,将栈顶结点B出栈,并将B置为当前节点,此时输出序列为DB;
由于B的右孩子不为空,根据操作3),将其右孩子E置为当前节点,由于E的左孩子为空,根据操作1),输出E,此时输出序列为DBE;
由于E的右孩子为空,根据操作4),执行出栈操作,将栈顶节点A出栈,并将节点A置为当前节点,此时输出序列为DBEA;
节点n的直接子节点为n的右孩子以及n的右孩子节点的所有左兄弟节点。所述节点的直接子节点为该节点的右孩子和该右孩子节点的所有左兄弟节点。这里是这样的,先压入根节点,然后判断左子树是否为空,不为空就压入栈,否则退出while循环之后就将null结点出栈,再判断当前栈是否为空,如果非空就出栈得到父节点然后判断右孩子,压入右孩子结点,再判断此右子树的左孩子是否为空,继续循环。
由于F的右孩子也为空,根据操作4),执行出栈操作,将栈顶元素C出栈,并将其置为当前节点,此时的输出序列为:DBEAFC;
由于C的右孩子为NULL,且此时栈空,根据操作5),遍历结束
void in_traverse(BTree pTree)
{
PSTACK stack = create_stack(); //创建一个空栈
BTree node_pop; //用来保存出栈节点
BTree pCur = pTree; //定义指向当前访问的节点的指针
//直到当前节点pCur为NULL且栈空时,循环结束
while(pCur || !is_empty(stack))
{
if(pCur->pLchild)
{
//如果pCur的左孩子不为空,则将其入栈,并置其左孩子为当前节点
push_stack(stack,pCur);
pCur = pCur->pLchild;
}
else
{
//如果pCur的左孩子为空,则输出pCur节点,并将其右孩子设为当前节点,看其是否为空
printf("%c ", pCur->data);
pCur = pCur->pRchild;
//如果为空,且栈不空,则将栈顶节点出栈,并输出该节点,
//同时将它的右孩子设为当前节点,继续判断,直到当前节点不为空
while(!pCur && !is_empty(stack))
{
pCur = getTop(stack);
printf("%c ",pCur->data);
pop_stack(stack,&node_pop);
pCur = pCur->pRchild;
}
}
}
}
3、后序遍历的非递归实现
那么下面就要说说先序遍历的非递归方式,遍历思想是这样的:先访问根节点在访问左节点, 最后访问右节点。最后就还剩下一种遍历方式,二叉树的后续遍历,后续遍历的思想是:先访问左孩子节点,然后在访问右孩子节点,最后访问根节点。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

对于任一节点P,
1)先将节点P入栈;
2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;
3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);
4)直到栈空,遍历结束。
下面以上图为例详细分析其后序遍历的非递归实现过程:
首先,设置两个指针:Cur指针指向当前访问的节点,它一直指向栈顶节点,每次出栈一个节点后,将其重新置为栈顶结点,Pre节点指向上一个访问的节点;
这里是这样的,先压入根节点,然后判断左子树是否为空,不为空就压入栈,否则退出while循环之后就将null结点出栈,再判断当前栈是否为空,如果非空就出栈得到父节点然后判断右孩子,压入右孩子结点,再判断此右子树的左孩子是否为空,继续循环。走遍迷宫:f1a(中间50000钱,有石)右下入----f1i左上,(回头)上半边左入----f1a右下,右上入----f1g左下,左半边上左入----f1c右下,(有石、冰)左下入----f1f右上,上半边右中入----f1a左上,左下入----f1h右上,左上入----f1b右下,(有冰)右上入----f1f下中,下半边回头下中入----f1b右上,(有冰)右下入----f1h左上,右中入----f1e左上,(有冰)右上入----f1i左下,右上入----f1d左下,左上入----f1g右中,右半边回头右中入----f1d左上,(中间牌下左边)传送----b1a(迷宫宝藏)。 节点n的直接子节点为n的右孩子以及n的右孩子节点的所有左兄弟节点。
由于B的也有左孩子和右孩子,根据操作3),将E、D依次入栈,Cur改为指向栈顶结点D;
4.译码的思想是循环读入一串哈夫曼序列,读到“0”从根结点的左孩子继续读,读到“1”从右孩子继续,如果读到一个结点的左孩子和右孩子是否都为0,如果是说明已经读到了一个叶子(字符),翻译一个字符成功,把该叶子结点代表的字符存在一个存储翻译字符的数组中,然后继续从根结点开始读,直到读完这串哈夫曼序列,遇到结束符便退出翻译循环。中序遍历需要先将当前结点(顶点)入栈,当前结点没有左儿子时,访问栈顶,并且栈内结点的头一个非空右儿子是下一个结点。b)判断结点node是否为空,若为空,则取出栈顶结点并出栈,将右孩子置为当前结点。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-111390-1.html
但是偏偏三哥就是不开窍
如果他们不怕付出巨大代价
更不能剥夺狗叫的权利
很喜欢舒淇