赞
踩
遍历定义
int a[5]={1,2,3,4,5};
int i;
for(i = 0;i < 5;i++ )
{
printf("%d ",a[i]);
//依次访问数组中的每个元素,就叫遍历。
}
return 0;
1 2 3 4 5
遍历目的
遍历用途
遍历方法
算法描述
若二叉树为空,则空操作;否则执行以下操作:
牢记按照 根左右 的顺序来进行遍历。每个结点的左子树的所有结点遍历完了之后才能轮到右子树。当一个结点的左右子树都为空的时候表示访问完毕。
若二叉树为空,则空操作;否则执行以下操作:
每个结点的左子树按照左根右的顺序访问完了之后,才能访问根结点,最后按照 左根右 的顺序访问该结点的右子树的所有结点。
每个结点都按照左右根的顺序访问结点,每个结点的左子树按照左右根的顺序访问完所有结点之后,再到该结点的右子树按照左右根的顺序访问所有结点,最后访问该结点。
若二叉树为空,则空操作;否则执行以下操作:
将所有非叶子结点看成根结点,然后按照各种每种遍历顺序进行遍历,就很轻松了。
【例1】
【例2】
已知二叉树的先序和中序序列,构造出相应的二叉树。
最后就剩个 J 结点,所以不用再往后分了。
至此,这棵二叉树构造完成。
已知一棵二叉树的中序和后序序列,请画出这棵二叉树。
举个栗子
算法步骤
执行过程
假设有这么一棵二叉树,指针 T 指向二叉树的根节点 A 。对以A为根的二叉树进行遍历。
//前趋(先序)遍历
void Pre(BiTree* T)
{
//二叉树及二叉树底下的所有子树中,
//某一棵树不为空时,执行以下操作
if(T != NULL)
{
printf("%d\n",T -> data);
//输出根节点的值
pre(T -> lchile);
//以同样的先序遍历的方法遍历左子树
pre(T -> rchile);
//以同样的先序遍历的方法遍历右子树
//当左、右子树的某个结点为空时,
//返回该结点的递归的上一层
}
}
先序序列:A B D C
从上图看:
从虚线出发到终点的路径上,每个结点经过三次。
第一次路过某个根结点的时候直接访问它,就相当于是先序遍历,如果将该结点的左子树访问完了之后,再回来访问该结点就是中序,后序同理。
时间复杂度
空间复杂度
二叉树中序遍历的非递归算法的关键:
算法步骤
举个例子
如下图所示的一棵二叉树的非递归遍历。
非递归遍历的算法描述
//中序遍历二叉树 T 的非递归算法
Status InOrderTraverse(BiTree T)
{
BiTree p;//用指针 p 指向要操作的结点(出入栈)
InitStack(S);
p = T;//先将二叉树的根结点赋给 p
q = new BiTNode;
//申请一个结点空间 q 用来存放栈顶弹出的元素,
//q的定义在另一个函数
//当p指向的结点为空,并且栈也为空的时候,退出循环
while(p || !StackEmpty(s))
{
if(p)// p 指向的当前的根结点不为空
{
push(S,p);//将当前的根结点入栈
p = p -> lchild;
//去访问该根结点的左子树
}
else// p 当当前结点的左孩子为空时
{
Pop(S,p);//首先将栈顶元素弹出,
//这样做的目的是能够比较好的对当前遍历树的位置进行定位,
//以至于能做到对该节点右子树的后续遍历操作
cout << q -> data;//访问根结点
p = q -> rchild;//遍历右子树
}
}
}
层次遍历:顾名思义就是按照二叉树的层数来遍历,第一层遍历完了之后遍历第二层,接着第三次以此类推。
对于一棵二叉树,从根结点开始,按照从上到下、从左到右的顺序访问每一个结点,且每个结点只访问一次。
算法思路:使用一个队列
举个栗子
算法实现
//定义一个顺序循环队列
typedef struct
{
BTNode data[MAX];//存放队中元素
int front,rear;//队头和队尾指针
}SqQueue;//顺序循环队列类型
层次遍历算法
按照先序遍历序列建立二叉树的二叉链表。
例:已知先序序列为:A B C D E G F
算法步骤
算法描述
//按照先序次序输入二叉树中结点的值(一个字符),
//创建二叉树链表表示的二叉树 T
Status CreateBiTree(BiTree &T)
{
scanf(&ch);//将输入的值放到ch里
if('#' == ch);//如如果遇到了 # 字符就将当前的树构造为空树
{
T = NULL;
}
else//递归创建二叉树
{
T = (BiTNode*)malloc(sizeof(BiTNode));
//向内存中申请一块结点空间,并将该空间的首地址赋给T
if(!T)//如果开辟空间成功则执行以下操作
{
exit(OVERFLOW);//生成根结点
}
T -> data = ch;
//将根节点的数据域置为输入的字符 ch
CreateBiTree(T -> lchild);
//以当前结点的左孩子域为参数,递归构造左子树
CreateBiTree(T -> rchild);
//以当前结点的右孩子域为参数,递归构造右子树
}
return OK;
}
算法步骤
算法描述
//复制一棵和 T 完全相同的二叉树
void Copy(BiTree T,BiTree &NEWT)
{
if(NULL == T)
{
//如果原来的树T是空树,递归结束
NewT = NULL;
return 0;
}
else
{
NewT = new BiTNode;
NewT -> data = T -> data;
//将根结点数据域的内容复制到新树的根结点
Copy(T -> lChile,NewT -> lchile);
//递归复制左子树
Copy(T -> rChile,NewT -> rchile);
//递归复制右子树
}
}
算法步骤
算法描述
//计算二叉树 T 的深度(这棵二叉树有几层)
int Depth(BiTree T)
{
if(NUll == T)//如果是空树,则深度为0,递归结束
{
return 0;
}
else
{
m = Depth(T -> lchild);//递归计算左子树的深度记为 m
n = Depth(T -> rchile);//递归计算右子树的深度为 n
if(m > n)//返回二叉树的深度 m 与 n 的较大的那个值+1
{
return m + 1;
}
else//m小于或等于n的时候都可以返回n+1
{
return n + 1;
}
}
}
算法描述
//统计二叉树 T 中结点的个数
int NodeCount (BiTree T)
{
//如果是空树,则结点个数为 0 ,递归结束
if(NULL == T)
{
return 0;
}
//反之返回结点个数为左子树的结点个数+右子树的结点个树再+1
else
{
return NodeCount(T -> lchile) + NodeCount(T - rchild)+1;
}
}
举个栗子
算法步骤
算法描述
//统计二叉树㕜的叶子结点的个数
int LeafCount(BiTree T)
{
//如果T为空树,则叶子结点为0
if(NULl == T)
{
return 0;
}
//如果该结点的左右孩子都为空,则此结点为叶子结点,是叶子结点那就发现了一个叶子结点返回1
if(T -> lchild == NULL && T -> rchild == NULL)
{
return 1;
}
else
{
return LeafCount(T -> lchild)+LeafCount(T -> rchild);
}
}
举个栗子
为什么要研究线索二叉树?
提出的问题
解决方法
如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前趋。如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继。-
对二叉树按某种遍历次序使其变位线索二叉树的过程叫做线索化。
举个栗子
为区分 lrchild 和 rchild 指针到底是指向孩子的指针,还是指针前趋或者后继的指针,对二叉链表中每个结点增加两个标志域 ltag
和 rtag
其中:
这样,结点结构就变成了:
二叉线索树结点类型定义
//二叉树的二叉线索存储表示
typedef struct BiThrNode
{
int data;//数据域,存储数据元素本身
int ltag,rtag;//左右标记域,存放 0 1
struct BiThrNode* lchild,rchild;//左右孩子指针
}BiThrNode,*BiThrTree;
先序线索二叉树
有了先序构造线索二叉树之后,中序、后序也是同样的道理。
中序线索二叉树
后序线索二叉树
小试牛刀
增加头结点
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。