二叉树递归非递归遍历.docx
《二叉树递归非递归遍历.docx》由会员分享,可在线阅读,更多相关《二叉树递归非递归遍历.docx(42页珍藏版)》请在冰豆网上搜索。
二叉树递归非递归遍历
《数据结构》——算法设计
学号:
姓名:
专业班级:
成绩:
评语:
设计1:
二叉树遍历
设计要求:
1.根据二叉树遍历思想,分别实现先序遍历、中序遍历、后序遍历、层次遍历的算法
2.遍历算法可以尝试使用递归或非递归的方法实现。
若需要使用堆栈或队列,请直接使用C++自带的stack和queue对象。
设计分析
(说明自己实现了哪几个算法,对每个算法的设计思想做简单扼要的说明)
建立Tree类
类中的元素有
TreeNode*Root;//树的根节点
boolFind;//查找开关
boolFound;//判断是否找到指定编号的树叶
intpos;//结点数量
ElemType*data;//结点值的数组
算法1.先序遍历的递归实现
(只需要写出核心代码和运行结果,对代码和结果做分析说明)
算法代码:
voidTree:
:
PreOrder(TreeNode*t)//protected先序遍历输出树中所有的值
{
if(t)
{
cout<data;
PreOrder(t->LeftChild);
PreOrder(t->RightChild);
}//if
}//PreOrder
voidTree:
:
PreOrder(boolenter)//public先序遍历
{
PreOrder(Root);
if(enter)cout<}//PreOrder
调用代码:
#defineElemTypeint
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t);//在编号为10的树结点的左孩子插入数字9
T1.PreOrder(true);
return0;
}
代码分析:
代码的访问顺序:
访问1,输出1;
访问2,输出2;
访问NULL(2的左孩子);
访问3,输出3;
访问5,输出5;
访问9,输出9;
访问NULL(9的左孩子);
访问NULL(9的右孩子);
访问NULL(5的右孩子);
访问6,输出6;
访问NULL(6的左孩子),访问NULL(6的右孩子);
访问4,输出4;
访问NULL(4的左孩子),访问NULL(4的右孩子);
输出1235964
运行结果:
算法2.先序遍历的非递归实现
算法代码:
voidTree:
:
Non_Recursive_PreOrder(TreeNode*T)//protected非递归先序遍历
{
/*——————————————————————————
首先进行p的输出。
存储的内容基本上是右结点。
遍历左边缘不断输出,然后转到右结点入栈,继续
左边缘不断输出。
等到再无左边缘的时候逆向输出
所有右孩子。
———————————————————————————*/
stacks;
TreeNode*p=T,*q;
s.push(p);//把Root推入栈内
while(!
s.empty())//如果s不空
{
cout<data;
q=p->RightChild;
if(q)s.push(q);//如果q(p的右孩子)不空则将q推入栈
p=p->LeftChild;
if(!
p)//如果p(p的左孩子)为空则回到(p的右孩子)
{
p=s.top();
s.pop();
}//if
}//while
}//Non_recursive_PreOrder
StatusTree:
:
Non_Recursive_PreOrder(boolenter)//非递归先序遍历
{
Non_Recursive_PreOrder(Root);
if(enter)cout<returnOK;
}//Non_recursive_PreOrder
调用代码:
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t);//在编号为10的树结点的左孩子插入数字9
T1.Non_Recursive_PreOrder(true);
return0;
}
代码分析:
不断遍历左孩子,如果有右孩子就入栈。
遍历到NULL的时候就读取栈首元素(即最近的一个右结点)。
然后重复以上过程。
访问1,输出1,4入栈;
访问2.输出2,3入栈;
访问NULL,3出栈;
访问3,输出3,6入栈;
访问5,输出5;
访问9;输出9;
访问NULL,6出栈;
访问6,输出6;
访问NULL,4出栈;
访问4,输出4;
运行结果:
算法3中序遍历的递归实现
算法代码:
voidTree:
:
InOrder(boolenter)//public中序遍历
{
InOrder(Root);
if(enter)cout<}//InOrder
voidTree:
:
InOrder(TreeNode*t)//protected中序遍历输出树中所有的值
{
if(t)
{
InOrder(t->LeftChild);
cout<data;
InOrder(t->RightChild);
}//if
}//InOrder
调用代码:
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t);//在编号为10的树结点的左孩子插入数字9
T1.InOrder(true);
return0;
}
代码分析:
访问1;
访问2,输出2;
访问3;访问5;
访问9,输出9;
访问5,输出5;
访问3,输出3;
访问6,输出6;
访问1,输出1;
访问4,输出4;
运行结果:
算法4中序遍历的非递归实现
算法代码:
StatusTree:
:
Non_Recursive_IndexOrder(boolenter)//非递归中序遍历
{
Non_Recursive_IndexOrder(Root);
if(enter)cout<returnOK;
}//Non_recursive_IndexOrder
voidTree:
:
Non_Recursive_IndexOrder(TreeNode*T)//protected非递归中序遍历
{
/*————————————————————————————
首先不断遍历左边缘但不输出只入栈,
直到碰到NULL后(此时p=NULL)返回父结点输出父结点。
然后转到右结点入栈,继续遍历左边缘不输出
只入栈。
一直遍历到某个度为0的右结点的右孩子的时候
结束一支分支的遍历。
另一支分支的遍历相同。
这一支分支的遍历
结束后中序遍历结束。
—————————————————————————————*/
stacks;
TreeNode*p=T;
s.push(NULL);
while
(1)
{
while(p)
{
s.push(p);//栈是回溯法的主要工具
p=p->LeftChild;
}//while遍历左边缘直到NULL
if(!
s.top())break;//如果栈到达栈底则跳出
p=s.top();//p=NULL的时候返回其父结点或爷结点
s.pop();//弹出父结点
cout<data;//输出父结点
p=p->RightChild;//调用父结点的右孩子
}//while
}//Non_recursive_IndexOrder
调用代码:
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t);//在编号为10的树结点的左孩子插入数字9
T1.Non_Recursive_IndexOrder(true);
return0;
}
代码分析:
入栈,访问左孩子,入栈,访问左孩子。
重复进行,一直到访问NULL。
获得父节点(即栈首元素),并输出值,然后访问右结点,如果有,则重复以上过程。
如果为NULL,则再次出栈输出值,继续访问右结点。
访问1,入栈;
访问2,入栈;
访问NULL(2的左孩子),2出栈,输出2;
访问3,入栈;
访问5,入栈;
访问9,入栈;
访问NULL(9的左孩子),9出栈,输出9;
访问NULL(9的右孩子),5出栈,输出5;
访问NULL(5的右孩子),3出栈,输出3;
访问6,入栈;
访问NULL(6的左孩子),6出栈,输出6;
访问NULL(6的右孩子),1出栈,输出1;
访问4,入栈;
访问NULL(4的左孩子),4出栈。
输出4;
访问NULL(4的右孩子),栈空,跳出循环;
运行结果:
算法5后序遍历的递归实现
算法代码:
voidTree:
:
PostOrder(boolenter)//public后序遍历
{
PostOrder(Root);
if(enter)cout<}//PostOrder
voidTree:
:
PostOrder(TreeNode*t)//protected后序遍历输出树中所有的值
{
if(t)
{
PostOrder(t->LeftChild);
PostOrder(t->RightChild);
cout<data;
}//if
}//PostOrder
调用代码:
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t);//在编号为10的树结点的左孩子插入数字9
T1.PostOrder(true);
return0;
}
代码分析:
访问1;
访问2;
访问NULL(2的左孩子);
访问3;
访问5;
访问9;
访问NULL(9的左孩子),访问NULL(9的右孩子),输出9;
访问5,访问NULL(5的右孩子),输出5;
访问3,访问6,访问NULL(6的左孩子),访问NULL(6的右孩子),输出6;访问3,输出3;
访问2,输出2;
访问1,访问4,访问NULL(4的左孩子),访问NULL(4的右孩子),输出4;
访问1,输出1;
运行结果:
算法6后序遍历的非递归实现
算法代码:
StatusTree:
:
Non_Recursive_PostOrder(boolenter)//非递归后序遍历
{
Non_Recursive_PostOrder(Root);
if(enter)cout<returnOK;
}//Non_Recursive_PostOrder
voidTree:
:
Non_Recursive_PostOrder(TreeNode*T)//protected非递归后序遍历
{
/*——————————————————————————
先进行左边缘遍历,入栈,并记载访问次数为1(tag=0)。
直到p=NULL,然后返回父结点,父结点访问次数为2(tag=1)。
p转到父结点的右孩子。
重复以上步骤。
如果栈顶结点被第三次访问,则输出该结点,弹出。
———————————————————————————*/
stacks;//用于存储树结点
stacks1;//用于存储结点暂离状态
TreeNode*p=T;
s.push(NULL);//没有此句函数直接结束
while
(1)//如果s不空
{
while(p)
{
s.push(p);
s1.push(false);
p=p->LeftChild;
}//while用于存储左边缘树结点,全部暂离
if(s1.top())
{
p=s.top();
s1.pop();
s.pop();
cout<data;
p=NULL;
}//if第二次访问结点结点已待命,将结点和状态全部弹出,输出结点。
令p=NULL,避免第四次读取结点。
else
{
s1.pop();
s1.push(true);
p=s.top()->RightChild;
}//else如果结点是暂离,让其准备,当第二次碰见它时就输出。
此时访问右孩子
if(!
s.top())break;//如果栈到达栈底则跳出
}//while
}//Non_recursive_PostOrder
调用代码:
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t);//在编号为10的树结点的左孩子插入数字9
T1.Non_Recursive_PostOrder(true);
return0;
}
代码分析:
(第一次访问结点入栈状态为false,第二次访问结点将状态改成true,第三次访问结点输出结点数据,避免出现第四次访问结点。
)
(为什么是这样?
访问父节点的时候设置状态为false,访问左孩子回到父节点状态改成true,从右孩子回到父节点的时候输出父节点,共三次。
)
非递归后序遍历的步骤:
①定义两个栈,第一个栈类型为第二个栈类型为,以及树节点指针p,将NULLpush进栈。
②循环,如果p不为空,则(树节点栈)p入栈,(状态栈)false入栈,表示还没进行第二次访问。
然后不断遍历左孩子。
③如果状态栈首元素为true(此时为第三次访问树节点),获得栈首树节点然后输出数据,出栈。
否则状态栈首元素为false(此时为第二次访问树节点),将状态改成false,然后访问其右孩子。
④如果到达栈底(NULL)就跳出无限循环。
①循环:
访问1,1入栈,false入栈;访问2,2入栈,false入栈;访问NULL,跳出循环;
由于栈首元素为false,false出栈,true入栈,然后访问2的右孩子;
②循环:
访问3,3入栈,false入栈;访问5,5入栈,false入栈;访问9,9入栈,false入栈;访问NULL,跳出循环。
由于栈首元素为false,false出栈,true入栈,然后访问9的右孩子
③循环:
NULL(9的右孩子),跳出循环
由于栈首元素为true,9出栈,true出栈,输出9,然后(TreeNode*)p赋值为NULL;
④循环:
p为NULL,跳出循环。
由于栈首元素是false,false换成true,访问5的右孩子;
⑤循环:
访问NULL(5的右孩子),跳出循环;
由于栈首元素是true,5出栈,true出栈,输出5,p=NULL;
⑥循环:
访问NULL,跳出循环;
由于栈首元素是false,false换成true,访问3的右孩子;
⑦循环:
访问6,6入栈,false入栈;访问NULL(6的左孩子),跳出循环;
由于栈首元素是false,false换成true,访问6的右孩子;
⑧循环:
访问NULL(6的右孩子),跳出循环;
由于栈首元素是true,6出栈,true出栈,输出6,p=NULL;
⑨循环:
NULL,跳出循环。
由于栈首元素是true,3出栈,true出栈,输出3,p=NULL;
⑩循环:
NULL,跳出循环。
由于栈首元素是true,2出栈,true出栈,输出2,p=NULL;
十一:
循环:
NULL,跳出循环;
false换成true;访问4
十二:
循环:
4入栈,false入栈;NULL(4的左孩子),跳出循环;
False换成true;访问NULL;
十三:
循环:
NULL,跳出循环;
4出栈,true出栈,输出4,p=NULL;
十四:
循环:
NULL,跳出循环;
1出栈,true出栈,输出1,p=NULL;
到达栈底,退出遍历。
运行结果:
算法7层次遍历
算法代码:
voidTree:
:
LevelOrder(TreeNode*t)//protected层次遍历
{
/*——————————————————
读取每一层的时候把下一层存进队列中。
———————————————————*/
TreeNode*p=t;
queueq;
do
{
if(p)
{
cout<data;
q.push(p->LeftChild);
q.push(p->RightChild);
}//if
p=q.front();
q.pop();
}//do
while(!
q.empty());
}//LevelOrder
voidTree:
:
LevelOrder(boolenter)//public层次遍历
{
LevelOrder(Root);
if(enter)cout<}//LevelOrder
调用代码:
intmain()
{
ElemTypedata[9]={1,2,NULL,3,NULL,NULL,4,NULL,NULL};
TreeT1;
TreeNode*t;
T1.MakeNode(t,5,true,NULL);
T1.InitTree(data,9);//根据data和数组长度为9建立二叉树
T1.insertLeftChild(5,t);//在编号为5的树结点的左孩子插入数字5
T1.MakeNode(t,6,true,NULL);
T1.insertRightChild(5,t);//在编号为5的树结点的右孩子插入数字6
T1.MakeNode(t,9,true,NULL);
T1.insertLeftChild(10,t)//在编号为10的树结点的左孩子插入数字9
T1.LevelOrder(true);
return0;
}
代码分析:
运行结果:
完整代码:
#include
#include
#include
#include