文章目录
遍历二叉树
- 遍历定义:顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次(又称周游)。
- 遍历目的:得到树中所有结点的一 个线性排列。
- 遍历用途:它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心。
- 遍历方法:依次遍历二叉树中的三个组成部分,便是遍历了整个二叉树。
1、遍历二叉树算法描述
假设:遍历左子树L;访问根结点D;遍历右子树R。
则遍历整个二叉树方案共有:
DLR、LDR、LRD、DRL、RDL、 RLD六种。
若规定先左后右,则只有前三种情况:
2、遍历二叉树操作定义
先序遍历
若二叉树为空,则空操作;否则
(1)访问根结点D;
(2)先序遍历左子树L;
(3)先序遍历右子树R。
中序遍历
若二叉树为空,则空操作;否则
(1)中序遍历左子树L;
(2)访问根结点D;
(3)中序遍历右子树R。
后序遍历
若二叉树为空,则空操作;否则
(1)后序遍历左子树L;
(2)后序遍历右子树R;
(3)访问根结点D。
例1:
例2:
3、递归遍历算法
先序遍历
Status PreOrderTraverse(BiTree T){
if(T==NULL)//空二叉树
return ok;
else
visit(T);//访问根结点,访问的形式不同,例如输出根结点printf("%d\t",T->data);
PreOrderTraverse(T->lchild);//递归遍历左子树
PreOrderTraverse(T->rchild);//递归遍历右子树
}
时间复杂度:O(n) //每个结点只访问一次
空间复杂度:O(n) //栈占用的最大辅助空间
中序遍历
Status InOrderTraverse(BiTree T){
if(T=NULL)//空二叉树
return OK;
else
InOrderTraverse(T->lchild);//递归遍历左子树
visit(T);//访问根结点,访问的形式不同,例如输出根结点printf("%d\t",T->data);
InOrderTraverse(T->rchild);//递归遍历右子树
}
时间复杂度:O(n) //每个结点只访问一次
空间复杂度:O(n) //栈占用的最大辅助空间
后序遍历
Status PostOrderTraverse(BiTree T){
if(T==NULL)
return OK;//空二叉树
else
PostOrderTraverse(T->lchild);//递归遍历左子树
PostOrderTraverse(T->rchild);//递归遍历右子树
visit(T);//访问根结点,访问的形式不同,例如输出根结点printf("%d\t",T->data);
}
时间复杂度:O(n) //每个结点只访问一次
空间复杂度:O(n) //栈占用的最大辅助空间
4、中序遍历非递归算法
二叉树中序遍历的非递归算法的关键:在中序遍历过某结点的整个左子树后,如何找到该结点的根以及右子树。
算法基本思想:使用一个栈
(1) 建立一个栈;
(2) 根结点进栈,遍历左子树;
(3) 根结点出栈,输出根结点,遍历右子树。
Status InOrderTraverse(BiTree T){
BiTree p;//p指向根结点
InitStack(S);
p=T;
while(p||!StackEmpty(S)){
if(p)
push(S,p);
p=p->lchild;
else
Pop(S,q);//q指向输出的结点
printf("%c",q->data);
p=q->rchild;
}
return OK;
}
5、层次遍历算法
对于一颗二叉树,从根结点开始,按从上到下、从左到右的顺序访问每一个结点。每一个结点仅仅访问一次。
算法基本思路:使用一个队列
(1) 建立一个队列;
(2) 将根结点进队;
(3) 队不空时循环:从队列中出列一个结点*p,访问它;若它有左孩子结点,将左孩子结点进队;若它有右孩子结点,将右孩子结点进队。
typedef struct{//使用队列类型定义
BTNode data[MaxSize];//存放队中元素
int front,rear;//队头和队尾指针
}SqQueue;//顺序循环队列类型
void LevelOrder(BTNode *b){
BTNode *p;
SqQueue *qu;
InitQueue(qu);//初始化队列
enQueue(qu,b);//根结点指针进入队列
while(!QueueEmpty(qu)){//队不为空,则循环
deQueue(qu,p);//出对结点p
printf("%c",p->data);//访问结点p
if(p->lchild!=NULL)//有左孩子时将其进队
enQueue(qu,p->lchild);
if(p->rchild!=NULL)//有右孩子时将其进队
enQueue(qu,p->rchild);
}
}
6、二叉树遍历算法的应用
二叉树的建立
Status CreateBiTree(BiTree &T){
scanf(&ch);//cin>>ch;
if(ch=="#")
T=NULL;
else
if(!(T=(BiTNode*)malloc(ziseof(BiTNode))))//T=new BiTNode
exit(OVERFLOW);
T->data=ch;//生成根结点
CreateBiTree(T->lchild);//构造左子树
CreateBiTree(T->rchild);//构造右子树
return OK;
} //CreateBiTree
复制二叉树
【算法步骤】
如果是空树,递归结束;
否则,申请新结点空间,复制根结点
递归复制左子树
递归复制右子树
int Copy(BiTree T,BiTree &NewT){
if(T==NULL)
NewT=NULL;
return 0;
else
NewT=new BiTNode;
NewT->data=T->data;
Copy(T->lchild,NewT->lchild);
Copy(T->rchild,NewT->rchild);
}
计算二叉树的深度
【算法步骤】
如果是空树,则深度为0;
否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m与n的较大者加1。
int Depth(BiTree T){
if(T==NULL)//如果是空树返回0
return 0;
else
m=Depth(T->lchild);
n=Depth(T->rchild);
if(m>n)
return (m+1);
else
return (n+1);
}
计算二叉树结点数
【算法步骤】
如果是空树,则结点个数为0;
否则,结点个数为左子树的结点个数+右子树的结点个数再+1。
int NodeCount(BiTree T){
if(T==NULL)
return 0;
else
return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
}
求叶子结点数
【算法步骤】
如果是空树,则叶子结点个数为0;
否则,为左子树的叶子结点个数+右子树的叶子结点个数。
int LeafCount(BiTree T){
if(T==NULL)
return 0;
if(T->lchild==NULL&&T->rchild==NULL)
return 1;//如果是叶子结点返回1
else
return LeafCount(T->lchild)+LeafCount(T->rchild);
}
线索二叉树
定义:
利用二叉链表中的空指针域。如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继。——这种改变指向的指针称为“线索”。
加上了线索的二叉树称为线索二叉树(Threaded Binary Tree)
对二叉树按某种遍历次序使其变为线索二叉树的过程叫线索化。
为区分Irchid和rchild指针到底是指向孩子的指针,还是指向前驱或者后继的指针,对二叉链表中每个结点增设两个标志域Itag和rtag,并约定:
Itag=0 Ichild指向该结点的左孩子
rtag=0 rchild指向该结点的右孩子
Itag=1 Ichild指向该结点的前驱
rtag=1 rchild指向该结点的后继
typedef struct BiThrNode{
int data;
int ltag,rtag;
struct BiThrNode *lchild,rchild;
}BiThrNode,*BiThrTree;
为避免悬空态,增设了一个头结点:
Itag=O,lchild指向根结点,
rtag=1,rchild指向遍历序列中最后一个结点,
遍历序列中第一个结点的Ic域和最后一个结点的rc域都指向头结点,这样处理起来更方便。