一、遍历二叉树
-
遍历定义:顺着某一条搜索路线巡防二叉树中的结点,使得到每个节点均被访问一次,而且仅被访问一次(又称周游)
-
访问的含义很广,可以是对结点作各种处理,
-
如:输出结点的信息、修该结点的数据值等,但要求这种访问不破坏原来的数据结构
-
-
遍历目的:得到树中所有结点的一个线性排列。
-
遍历用途:它是树结构插入、删除、修该、查找和排序运算的前提,是二叉树一切运算的基础和核心。
1.1 先序遍历
若二叉树为空,则返回空;否则
-
访问根节点
-
先序遍历左子树
-
先序遍历右子树
代码实现:
// 先序遍历
int PreOrderTraverse(BiTree T){
if(T == NULL){
return 0; // 空二叉树
}
else{
printf("%c\t",T->data); // 访问根结点
PreOrderTraverse(T->Lchild); // 递归遍历左子树
PreOrderTraverse(T->Rchild); // 递归遍历右子树
}
}
1.2 中序遍历
若二叉树为空,则返回空;否则
-
中序遍历左子树
-
访问根节点
-
中序遍历右子树
代码实现:
// 中序遍历
int InorderTraverse(BiTree T){
if(T == NULL){ // 空二叉树
return 0;
}
else{
InorderTraverse(T->Lchild); // 递归遍历左
printf("%c\t",T->data); // 访问根结点
InorderTraverse(T->Rchild); // 递归遍历右
}
}
1.3 后续遍历
若二叉树为空,则返回空操作;否则:
-
后续遍历左子树
-
后序遍历右子树
-
访问根节点
代码实现:
// 后序遍历
int PostOrderTraverse(BiTree T){
if(T == NULL){ // 空二叉树
return 0;
}
else{
PostOrderTraverse(T->Lchild); // 递归遍历左
PostOrderTraverse(T->Rchild); // 递归遍历右
printf("%c\t",T->data); // 访问根结点
}
}
1.4 例题
1.5 根据遍历序列确定二叉树
-
若二叉树中各节点的值均不相同,则二叉树结点的先序序列、中序序列、后序序列都是唯一的
-
由二叉树的先序序列和中序序列,或由二叉树的后续序列和中序序列可以确定唯一 一颗二叉树
1.6 中序遍历的非递归实现
基本思想:
-
先建立一个栈
-
根结点进栈,遍历左子树
-
根结点出栈,输出根节点,遍历右子树。
1.6.1 先建立一个栈
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
// 1、栈的初始化
Status InitLinkStack(LinkStack &S){
S = NULL;
return OK;
}
// 2、链栈是否为空
Status LinkStackEmpty(LinkStack &S){
if(S == NULL){
return TRUE;
}else{
return FALSE;
}
}
// 3、 链栈的入栈
Status Push(LinkStack &S,SElemType e){
StackNode *p;
p = new StackNode;
p->data = e;
p->next = S;
S = p;
return OK;
}
// 4、链栈的出栈
Status Pop(LinkStack &S,SElemType e){
StackNode *p;
if(S == NULL){
return ERROR;
}
e = S->data;
p = S;
S = S->next;
delete p;
return OK;
}
1.6.2 建立二叉树
// 构造一个二叉树
typedef struct BiNode{
SElemType data;
struct BiNode *Lchild;
struct BiNode *Rchild;
}BiNode,*BiTree;
1.6.3 遍历二叉树
// 中序遍历非递归
Status InOrderStraverse(BiTree T){
BiTree p,q;
LinkStack S;
InitLinkStack(S);
p = T;
while(p||!LinkStackEmpty(S)){
if(p){
Push(S,p->data);
p = p->Lchild;
}
else{
Pop(S,q->data);
printf("%c\t",p->data);
p = q->Rchild;
}
}
return OK;
}
1.7 二叉树的层次遍历
基本思想:
-
使用一个队列
-
将根结点进队;
-
队不空时循环:从队列中出列一个结点*p,访问它;
-
若它有左孩子结点,将左孩子结点进队;
-
若它有右孩子结点,将右孩子结点进队;
-
# define OK 1
# define ERROR 0
# define MAXQSIZE 100
typedef char QElemType;
typedef int Status;
typedef struct {
QElemType data[MAXQSIZE];
int front;
int rear;
}SqQueue;
// 1、初始化
Status InitQueue(SqQueue &Q){
Q.front = 0;
Q.rear = 0;
return OK;
}
Status QueueEnpty(SqQueue &Q){
if(Q.front == Q.rear){
return 1;
}
else{
return 0;
}
}
// 2、循环队列入队
Status EnQueue(SqQueue &Q,QElemType e){
if((Q.rear+1)%MAXQSIZE == Q.front){ // 队满
return ERROR;
}
Q.data[Q.rear] = e; // 新元素加入队尾
Q.rear = (Q.rear+1)%MAXQSIZE; // 队尾指针+1
return OK;
}
// 3、出队
Status DeQueue(SqQueue &Q,QElemType e){
if(Q.front == Q.rear){
return ERROR; // 队空
}
e = Q.data[Q.front];
Q.front = (Q.front+1)%MAXQSIZE;
return OK;
}
// 构造一个二叉树
typedef struct BiNode{
QElemType data;
struct BiNode *Lchild;
struct BiNode *Rchild;
}BiNode,*BiTree;
// 二叉树的层次遍历算法
void LevelOrder(BiNode *b){
BiNode *p;
SqQueue qu;
InitQueue(qu); // 初始化队列
EnQueue(qu,b->data); // 根节点指针进入队列
while(!QueueEnpty(qu)){ // 队列不为空
DeQueue(qu,p->data); // 出队结点
printf("%c\t",p->data);
if(p->Lchild != NULL){
EnQueue(qu,p->Lchild->data); // 左孩子进队
}
if(p->Rchild != NULL){
EnQueue(qu,p->Rchild->data); // 右孩子进队
}
}
}
二、二叉树遍历算法的应用
2.1 二叉树的建立
// 二叉树的创建
void CreateBiTree(BiTree &T){
char ch;
scanf("%c",&ch);
if(ch == '#'){
T = NULL;
}
else{
T = (BiTree )malloc(sizeof(BiNode));
if(!T){
exit(-1);
}
T->data = ch;
CreateBiTree(T->Lchild);
CreateBiTree(T->Rchild);
}
}
2.2 二叉树的复制
// 复制二叉树
int Copy(BiTree T,BiTree &newT){
if(T==NULL){ // 如果是空树,返回0
newT = NULL;
return 0;
}
else{
newT = new BiNode;
newT->data = T->data;
Copy(T->Lchild,newT->Lchild);
Copy(T->Rchild,newT->Rchild);
}
}
2.3 计算二叉树的深度
// 计算二叉树的深度
int Depth(BiTree T){
int m,n;
if(T==NULL){
return 0;
}
else{
m = Depth(T->Lchild);
n = Depth(T->Rchild);
if(m>n){
return (m+1);
}
else{
return (n+1);
}
}
}
2.4 计算二叉树的结点总数
// 计算结点总数
int NodeCount(BiTree T){
if(T == NULL){
return 0;
}
else{
return NodeCount(T->Lchild)+NodeCount(T->Rchild
)+1;
}
}
2.5 计算叶子节点数
// 计算叶子节点数
int LeadCount(BiTree T){
if(T ==NULL){
return 0;
}
if(T->Lchild == NULL && T->Rchild == NULL){
return 1;
}
else{
return LeadCount(T->Lchild)+LeadCount(T->Rchild);
}
}
三、线索二叉树
利用二叉链表中的空指针域:
-
如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;
-
如果某个结点的右孩子为空,则将空的右孩子指针域改为指向其后继
-
这种改变指向的指针称为
线索
-
加上了线索的二叉树称为线索二叉树
-
对二叉树按某种遍历次序使其变为线索二叉树的过程叫做线索化
对二叉链表中每个结点增设两个标志域Ltag 和 Rtag,并约定
Ltag = 0 Lchild 指向该节点的左孩子
Ltag = 1 Lchild 指向该节点的前驱
Rtag = 0 Rchild 指向该节点的右孩子
Rtag = 1 Rchild 指向该节点的后继