二叉树的遍历
2015-12-04 20:02
260 查看
遍历二叉树(traversing binary tree)
1.遍历二叉树的描述
遍历二叉树是指按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。遍历二叉树是二叉树最基本的操作,
也是二叉树其他各种操作的基础、遍历的实质是对二叉树进行线性化的过程,即遍历的结果是将非线性结构的树中结点排成一个线性序列。
1.先序遍历二叉树的操作定义如下:
若二叉树为空,则空操作,否则:
1)访问根结点;
2)先序遍历左子树;
3)先序遍历右子树;
2.中序遍历二叉树的操作定义如下:
若二叉树为空,则空操作,否则:
1)中序遍历左子树;
2)访问根结点;
3)中序遍历右子树;
3.后序遍历二叉树的操作定义如下:
若二叉树为空,则空操作,否则:
1)后序遍历左子树;
2)后序遍历右子树;
3)访问根结点;
例如,如下图所示的二叉树表示下述表达式
a+b*(c-d)-e/f
中序遍历二叉树基本操作的递归算法在二叉链表上的实现,算法将结点的访问简化为数据的输出。
中序遍历的递归算法
[算法描述]
可以利用栈将递归算法改成非递归算法,例如,从中序遍历递归算法执行过程中递归工作栈的状态可见:
1.工作记录中包含两项,其一是递归调用的语句编号,其二数指向根结点的指针,则当栈顶记录中的指针非空时,应遍历左子树,即指向左子树根的指针进栈。
2.若栈顶记录中的指针值为空,应该退至上一层,若是从左子树返回,则应访问当前层(即栈顶记录)中指针所指的根结点。
3.若是从右子树返回,则表明当前层遍历结束,应该继续退栈。从另一个角度看,这意味着遍历右子树时不需要保存当前层的根指针,直接修改栈顶记录中的指针即可。
中序遍历的非递归算法
[算法思想]
设S为一个栈,p为指向根结点的指针。
1)当p非空时,将p所指向的地址进栈,p指向该结点的左孩子
2)当p为空时,弹出栈顶元素并访问,将p指向该结点的右孩子
3)重复前两步,直到栈空且p也为空
[算法描述]
2.根据遍历序列确定二叉树
由二叉树的先序序列和中序序列,或由其后序序列和中序序列均能唯一确定一棵二叉树。
例如,已知一棵二叉树的中序序列和后序序列分别是
BDCEAFHG 和 DECBHGFA,请画出这棵二叉树。
[解题思路]
1)由后序遍历特征,根结点必在后序序列尾部,即根结点是A;
2)由中序遍历特征,根结点必在其中间,而且其左部必全部是左子树子孙(BDCE),其右部必全部是右子树子孙(FHG).
3)继而,根据后序中的DECB子树可确定B为A的左孩子,根据HGF子串可确定F为A的右孩子;依次类推,可以唯一确定一棵二叉树。
但是,由一棵二叉树的先序序列和后序序列不能唯一确定一棵二叉树,因为无法确定左右子树两部分。
例如,如果有先序序列AB,后序序列BA,因为无法确定B为左子树还是右子树,所以可得到图所示的两颗不用的二叉树。
3.二叉树遍历算法的应用
“遍历”是二叉树各种操作的基础,假设访问结点的具体操作不仅仅局限于输出结点数据域的值,而把“访问”延伸到对结点的判别、计数等其他操作
,可以解决一些关于二叉树的其他实际问题。如在遍历过程中生成结点,这样便可以建立二叉树的存储结构。
1)创建二叉树的存储结构——二叉链表
为简化问题,设二叉树中结点的元素均为一个单字符。假设按先序遍历的顺序建立二叉链表,T为指向根结点的指针:首先输入一个根结点,若输入是一个
“#”字符,则表明该二叉树为空树,即T为NULL;否则输入的该字符赋给T->data,之后依次递归建立它的左子树T->lchild和右子树T->rchild。例如下图,读入
字符的顺序为:ABC ## DE # G ## F ###(其中#表示空树),可建立相应的二叉链表。
先序遍历的顺序建立二叉链表
[算法描述]
2)计算二叉树的深度
二叉树的深度为树中结点的最大层次。如果是空树,则深度为0;否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m
与n的较大者加1.显然,这是在后序遍历二叉树的基础上进行的运算。
[算法描述]
3)统计二叉树中结点的个数
如果是空树,则结点个数为0;否则,结点个数为 左子树的结点个数加上右子树的结点个数再加上1。
[算法描述]
[完整代码]
1.遍历二叉树的描述
遍历二叉树是指按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。遍历二叉树是二叉树最基本的操作,
也是二叉树其他各种操作的基础、遍历的实质是对二叉树进行线性化的过程,即遍历的结果是将非线性结构的树中结点排成一个线性序列。
1.先序遍历二叉树的操作定义如下:
若二叉树为空,则空操作,否则:
1)访问根结点;
2)先序遍历左子树;
3)先序遍历右子树;
2.中序遍历二叉树的操作定义如下:
若二叉树为空,则空操作,否则:
1)中序遍历左子树;
2)访问根结点;
3)中序遍历右子树;
3.后序遍历二叉树的操作定义如下:
若二叉树为空,则空操作,否则:
1)后序遍历左子树;
2)后序遍历右子树;
3)访问根结点;
例如,如下图所示的二叉树表示下述表达式
a+b*(c-d)-e/f
中序遍历二叉树基本操作的递归算法在二叉链表上的实现,算法将结点的访问简化为数据的输出。
中序遍历的递归算法
[算法描述]
void InOrderTraverse(BiTree T) { /*中序遍历二叉树T的递归算法*/ if(T) { InOrderTraverse(T->lchild); cout<<T->data; InOrderTraverse(T->rchild); } }只要改变输出语句的顺序,便可类似地实现先序遍历和后序遍历的递归算法。
可以利用栈将递归算法改成非递归算法,例如,从中序遍历递归算法执行过程中递归工作栈的状态可见:
1.工作记录中包含两项,其一是递归调用的语句编号,其二数指向根结点的指针,则当栈顶记录中的指针非空时,应遍历左子树,即指向左子树根的指针进栈。
2.若栈顶记录中的指针值为空,应该退至上一层,若是从左子树返回,则应访问当前层(即栈顶记录)中指针所指的根结点。
3.若是从右子树返回,则表明当前层遍历结束,应该继续退栈。从另一个角度看,这意味着遍历右子树时不需要保存当前层的根指针,直接修改栈顶记录中的指针即可。
中序遍历的非递归算法
[算法思想]
设S为一个栈,p为指向根结点的指针。
1)当p非空时,将p所指向的地址进栈,p指向该结点的左孩子
2)当p为空时,弹出栈顶元素并访问,将p指向该结点的右孩子
3)重复前两步,直到栈空且p也为空
[算法描述]
void InOrderTraverse(BiTree T) { /*中序遍历二叉树T的非递归算法*/ InitStack(S);p=T; q=new BiTNode; while(p || !StackEmpty(S) ) { if(p) /*p非空根指针进栈,遍历左子树*/ { Push(S,p); p=p->lchild; } else /*p为空根指针退栈,访问根结点,遍历右子树*/ { Pop(S,q); cout<<q->data; p=q->rchild; } } }
2.根据遍历序列确定二叉树
由二叉树的先序序列和中序序列,或由其后序序列和中序序列均能唯一确定一棵二叉树。
例如,已知一棵二叉树的中序序列和后序序列分别是
BDCEAFHG 和 DECBHGFA,请画出这棵二叉树。
[解题思路]
1)由后序遍历特征,根结点必在后序序列尾部,即根结点是A;
2)由中序遍历特征,根结点必在其中间,而且其左部必全部是左子树子孙(BDCE),其右部必全部是右子树子孙(FHG).
3)继而,根据后序中的DECB子树可确定B为A的左孩子,根据HGF子串可确定F为A的右孩子;依次类推,可以唯一确定一棵二叉树。
但是,由一棵二叉树的先序序列和后序序列不能唯一确定一棵二叉树,因为无法确定左右子树两部分。
例如,如果有先序序列AB,后序序列BA,因为无法确定B为左子树还是右子树,所以可得到图所示的两颗不用的二叉树。
3.二叉树遍历算法的应用
“遍历”是二叉树各种操作的基础,假设访问结点的具体操作不仅仅局限于输出结点数据域的值,而把“访问”延伸到对结点的判别、计数等其他操作
,可以解决一些关于二叉树的其他实际问题。如在遍历过程中生成结点,这样便可以建立二叉树的存储结构。
1)创建二叉树的存储结构——二叉链表
为简化问题,设二叉树中结点的元素均为一个单字符。假设按先序遍历的顺序建立二叉链表,T为指向根结点的指针:首先输入一个根结点,若输入是一个
“#”字符,则表明该二叉树为空树,即T为NULL;否则输入的该字符赋给T->data,之后依次递归建立它的左子树T->lchild和右子树T->rchild。例如下图,读入
字符的顺序为:ABC ## DE # G ## F ###(其中#表示空树),可建立相应的二叉链表。
先序遍历的顺序建立二叉链表
[算法描述]
void CreateBiTree(BiTree &T) { /*按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树T*/ cin>>ch; if(ch == '#') T=NULL; else { T=new BiTNode; T->data=ch; CreateBiTree(T->lchild); CreateBiTree(T->rchild); } }
2)计算二叉树的深度
二叉树的深度为树中结点的最大层次。如果是空树,则深度为0;否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m
与n的较大者加1.显然,这是在后序遍历二叉树的基础上进行的运算。
[算法描述]
int Depth(BiTree T) { if(T == NULL) return 0; /*如果是空树,深度为0,递归结束*/ else { m=Depth(T->lchild); /*递归计算左子树的深度记为m*/ n=Depth(T->rchild); /*递归计算右子树的深度记为n*/ return m>n?m+1:n+1; /*二叉树的深度为m与n的较大者加1*/ } }
3)统计二叉树中结点的个数
如果是空树,则结点个数为0;否则,结点个数为 左子树的结点个数加上右子树的结点个数再加上1。
[算法描述]
int NodeCount(BiTree T) { if(T == NULL)return 0; /*如果是空,则结点个数为0,递归结束*/ else /*否则结点个数为左子树的结点个数+右子树的结点个数+1*/ { return NodeCount(T->lchild)+NodeCount(T->rchild)+1; } }
[完整代码]
#include<iostream>[运行结果]
using namespace std;
typedef struct BiTNode
{
char data; /*结点数据域*/
struct BiTNode *lchild,*rchild; /*左右孩子指针*/
}BiTNode,*BiTree;
int n,m;
void CreateBiTree(BiTree &T)
{
char ch;
/*按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树T*/
cin>>ch;
if(ch == '#') T=NULL;
else
{
T=new BiTNode;
T->data=ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
int Depth(BiTree T) { if(T == NULL) return 0; /*如果是空树,深度为0,递归结束*/ else { m=Depth(T->lchild); /*递归计算左子树的深度记为m*/ n=Depth(T->rchild); /*递归计算右子树的深度记为n*/ return m>n?m+1:n+1; /*二叉树的深度为m与n的较大者加1*/ } }
int NodeCount(BiTree T) { if(T == NULL)return 0; /*如果是空,则结点个数为0,递归结束*/ else /*否则结点个数为左子树的结点个数+右子树的结点个数+1*/ { return NodeCount(T->lchild)+NodeCount(T->rchild)+1; } }
void InOrderTraverse(BiTree T)
{
/*中序遍历二叉树T的递归算法*/
if(T)
{
InOrderTraverse(T->lchild);
cout<<T->data<<" ";
InOrderTraverse(T->rchild);
}
}
int main()
{
BiTree T;
cout<<"Input the Binary Tree 's node:"<<endl;
/*sample input:ABC ## DE # G ## F ###*/
CreateBiTree(T);
cout<<"InOrderTraverse: "<<endl;
InOrderTraverse(T);
cout<<"\nThe Depth:"<<Depth(T)<<"\t The sum's node:"<<NodeCount(T)<<endl;
getchar();
return 0;
}
相关文章推荐
- bzoj1188 分裂游戏 博弈论
- 模拟也有动脑子,类似蛇形填数
- linux (Centos7) 编译安装nginx
- 四边形密铺平面
- HDU 1312 Red and Black --- 入门搜索 DFS解法
- 算法竞赛 之果园里的树
- Java查找数组中的最小值
- hdu2196 树形dp
- 青海湖
- ORA-00439: feature not enabled: Managed Standby
- HDU 5057 Argestes and Sequence 分块
- Matlab中画圆
- 检测调试器和trace
- 后台定位上传的代码实践
- 监督学习和无,监督学习区别
- V$SESSION_LONGOPS
- 开始了我的记录生活之旅!
- ext2/3文件系统存储,查找,新建,删除文件机制详解
- mysql key index区别
- malloc与free对被操作内存的影响