数据结构P4.3:二叉树的遍历
什么是遍历
按
照某种次序
把所有结点都访问一边线性结构
中:对于链表、队列都有从前往后
或者从后往前
的依次遍历树
的结构中:基于树的层次特性确定的次序规则可以是层次遍历
- 树的
先序遍历
:按照根节点->左节点->右节点
顺序依次遍历 - 树的
中序遍历
:按照左节点->根节点->右节点
顺序依次遍历 - 树的
后序遍历
:按照左节点->右节点->根节点
顺序依次遍历
二叉树的先序遍历
- 遍历顺序:先访问根结点,再访问左右结点
- 算法思想:利用递归,访问每层的根结点、左结点、右结点
//二叉树的先序遍历代码伪代码
1.从整棵树BiTree的根结点T开始访问T的左结点、右节点
2.如果左节点有子结点,把左结点当作所在层的根结点,循环上述步骤直至左右结点为空
3.右结点一样的方式
- 算法步骤
1.若二叉树为空,则无需操作
2.若二叉树非空
1.访问根结点
2.先序遍历左子树
3.先序遍历右子树
- 图解
代码实现
//二叉树的先序遍历代码
//二叉树的结点(链式存储)
typedef struct BiTNode{
char data;
struct BiTNode *lchild, *rchild; /*左右子结点*/
}BiTNode,*BiTree;
//先序遍历
void PreOrder(BiTree T){
if(T!=NULL){
visit(T) /*访问根节点*/
PreOrder(T->lchild); /*递归遍历左子树,结束条件为遇到空结点*/
PreOrder(T->rchild); /*递归遍历右子树,开始条件递归遍历左子树,结束条件为遇到空结点*/
}
}
二叉树的中序遍历
- 遍历顺序:先访问左结点,再访问根结点、右结点
- 算法思想:利用递归,访问每层的左结点、根结点、右结点
//二叉树的先序遍历代码伪代码
1.从整棵树BiTree的根结点T的左结点开始访问根结点、右结点
2.如果左节点有子结点,把左结点当作所在层的根结点,循环上述步骤直至左右结点为空
3.右结点一样的方式
- 算法步骤
1.若二叉树为空,则无需操作
2.若二叉树非空
1.先序遍历左子树
2.访问根节点
3.先序遍历右子树
- 图解
代码实现
//二叉树的中序遍历代码
void InOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild); /*递归遍历左子树,结束条件为遇到空结点*/
visit(T) /*访问根节点*/
InOrder(T->rchild); /*递归遍历右子树,开始条件递归遍历左子树,结束条件为遇到空结点*/
}
}
二叉树的后序遍历
- 遍历顺序:先访问左右结点,最后访问根结点
- 算法思想:利用递归,访问每层的左结点、右结点、根结点
//二叉树的先序遍历代码伪代码
1.从整棵树BiTree的根结点T的右结点开始访问左结点、根结点
2.如果左结点有子结点,把左结点当作所在层的根结点,循环上述步骤直至左右结点为空
3.右结点一样的方式
- 算法步骤
1.若二叉树为空,则无需操作
2.若二叉树非空
1.先序遍历左子树
2.先序遍历右子树
3.访问根节点
- 图解
代码实现
//二叉树的后序遍历代码
void PostOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild); /*递归遍历左子树,结束条件为遇到空结点*/
InOrder(T->rchild); /*递归遍历右子树,开始条件递归遍历左子树,结束条件为遇到空结点*/
visit(T) /*访问根节点*/
}
}
二叉树的层序遍历
-
下面这样的一棵树,应该怎么样实现它的遍历?
-
需要从第一层的 根结点开始一层层的进行遍历,按照访问上层父节点的子结点这样的规则逐层遍历
算法思想
- 借助
辅助队列
来存放要访问的结点,初始化这个辅助队列 - 根结点先入队
- 循环条件:若队列为非空,则队头结点出队(FIFO),访问的就是该父结点,然后将其左、右子节点插入队尾后续访问(如果存在)
- 重复步骤3直至队列为空
- 图解
代码实现
//链式队列结点定义
typedef struct LinkNode{ /*链式队列的结点*/
BiTNode *data; /*数据域,类型为树的结点指针,而不是结点,节省空间*/
struct LinkNode *next; /*指向下一个结点的指针*/
}LinkNode;
typedef struct{ /*链式队列的定义*/
LinkNode *head,*tail; /*队列的队头和队尾指针*/
}LinkQueue;
//带头结点的链式队列初始化
bool InitLinkQueue(&Q){
LinkNode *L=(LinkNode*)malloc(sizeof(LinkNode)); /*声明一个头结点指针L并分配空间*/
Q.head=Q.tail=L; /*初始化时,队头,队尾都指向头结点*/
}
//判断是否空队列(带头结点)
bool IsEmptyQueue(Q){
if(Q.tail==Q.head)
return true; /*队空*/
else
return false; /*队非空*/
}
//插入,新元素入队(带头结点)
void EnLinkQueue(LinkQueue &Q,ElemType x){
LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode)); /*声明一个新结点指针s并分配空间*/
s->data=x; /*将要插入的元素赋值给新节点的数据域*/
s->next=NULL; /*新结点s是要插入到队尾的,因此指向NULL*/
Q.tail->next=s; /*新的结点插入到当前队列的Q的队尾,相当于当前Q的队尾指针的下一个结点是s*/
Q.tail=s; /*修改队尾指针,结点s为Q的新队尾*/
}
//删除,队头元素出队(带头结点)
bool DeLinkQueue(LinkQueue &Q,ElemType &x){
if(Q.tail==Q.head) /*带头结点空队判断条件*/
return false;
LinkNode *p=Q.head->next /*删除的结点p是当前队列的头结点的下一个结点(当前队头head=Q.head->next)*/
x=p->data; /*变量x返回当前结点p(当前队头)数据*/
Q.head->next=p->next; /*修改当前Q的队头head指针为p结点的下一个结点*/
//等价于:Q.head->next=Q.head->next->next;
if(p==Q.tail) /*删除的结点p如果是队尾*/
Q.tail=Q.head; /*修改tail指针*/
free(p); /*释放结点p*/
return true;
}
//层序遍历
void LevelOrder(BiTree T){
LinkQueue Q; /*声明一个辅助队列Q*/
InitLinkQueue(Q); /*初始化辅助队列Q*/
BiTree p;
EnLinkQueue(Q,T); /*根结点入队*/
while(!IsEmptyQueue(Q)){ /*只要队列不为空就循环*/
DeLinkQueue(Q,p) /*队头结点出队*/
visit(p); /*访问出队的结点,打印结点的值等等*/
if(p->lchild!=NULL)
EnLinkQueue(Q,p->lchild); /*出队结点的左孩子入队*/
if(p->rchild!=NULL)
EnLinkQueue(Q,p->rchild); /*出队结点的右孩子入队*/
}
}
由遍历序列构造二叉树
- 一种遍历序列会对应多种二叉树形态
- 由于 中序+任意序列即可确定一颗二叉树的形态