总目录:https://blog.csdn.net/treesorshining/article/details/125726400
1.说明
层序遍历即是一层一层从左至右访问二叉树每个结点,类似于广度优先遍历。
实现思路:借助一个辅助队列,从根节点开始,根结点入队,访问此结点后出队,并将此结点的左右孩子入队(如果有),再依次对队头结点执行上述操作,直至队列为空为止。
2.算法实现
// 层序遍历
// 即一层一层从左至右访问每个结点,即广度优先遍历
// 此操作需要一个辅助队列,从根结点开始入队,访问此结点后出队,并将此结点的左右孩子入队(如果有)
// 再对每一个入队结点重复上述操作直至遍历完成(队列空)
void LevelOrder(BiTree T) {
LinkQueue Q;
// 初始化辅助队列
InitQueue(Q);
BiTree p;
// 根节点入队
EnQueue(Q, T);
// 队列不空则一直循环
while(!IsEmpty(Q)) {
// 队头结点出队
DeQueue(Q, p);
// 访问出队结点
visit(p);
// 访问出队结点
if(p->lchild != NULL) {
// 左孩子入队
EnQueue(Q, p->lchild);
}
if(p->rchild != NULL) {
// 右孩子入队
EnQueue(Q, p->rchild);
}
// 释放空间
free(p);
}
}
3.完整代码
#include<stdio.h>
#include<stdlib.h>
typedef struct BiTNode {
// 数据域
char data;
// 左、右孩子指针
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
typedef struct LinkNode { // 链式队列结点
// 此处存的是指针而不是结点,可节省大量空间
BiTNode* data; // 数据域
struct LinkNode *next; // 指针域
} LinkNode;
typedef struct { // 链式队列
// 如果不设置尾指针,若要插入,遍历到队尾再插入,时间复杂度O(n)
LinkNode *front, *rear; // 队列的队头和队尾指针
} LinkQueue;
// 初始化
void InitTree(BiTree &T) {
// 初始化可将根节点置空,空树
T = NULL;
// 插入根节点
// 分配空间
T = (BiTree)malloc(sizeof(BiTree));
// 赋值
T->data = 'A';
// 初始无左右子树
T->lchild = NULL;
T->rchild = NULL;
}
// 左插入结点
bool InsertLeftTreeNode(BiTNode* &T, char x) {
// 分配空间
BiTNode *p = (BiTNode*)malloc(sizeof(BiTNode));
// 分配空间失败的情况
if(p == NULL) {
return false;
}
// 数据域赋值
p->data = x;
// 初始插入,无左右孩子,置空
p->lchild = NULL;
p->rchild = NULL;
// 作为指定插入结点的左孩子
T->lchild = p;
return true;
}
// 右插入结点
bool InsertRightTreeNode(BiTNode* &T, char x) {
// 分配空间
BiTNode *p = (BiTNode*)malloc(sizeof(BiTNode));
// 分配空间失败的情况
if(p == NULL) {
return false;
}
// 数据域赋值
p->data = x;
// 初始插入,无左右孩子,置空
p->lchild = NULL;
p->rchild = NULL;
// 作为指定插入结点的左孩子
T->rchild = p;
return true;
}
// 访问结点
void visit(BiTree T) {
printf("%c ", T->data);
}
// 树的深度
int treeDepth(BiTree T) {
// 根结点为空,则深度为0
if(T == NULL) {
return 0;
} else {
// 递归遍历左子树,获得左子树深度
int l = treeDepth(T->lchild);
// 递归遍历右子树,获得右子树深度
int r = treeDepth(T->rchild);
// 树的深度=Max{左子树深度,右子树深度} + 1
// 加1相当于访问根节点,包括根结点深度+1
return l > r ? l + 1 : r + 1;
}
}
// 初始化(带头结点)
void InitQueue(LinkQueue &Q) {
// 初始时为头结点分配空间,并令头指针、尾指针都指向头结点
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
// 将头结点指针域置空
Q.front->next = NULL;
}
// 判断队列是否为空
bool IsEmpty(LinkQueue Q) {
// 头指针与尾指针指向同一地址(头结点即表示队列为空)
// 或者也可以根据Q.front->next = NULL来进行判定
if(Q.front == Q.rear) {
return true;
} else {
return false;
}
}
// 入队
bool EnQueue(LinkQueue &Q, BiTNode *x) {
// 申请分配空间
LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
// 内存分配失败的情况
if(s == NULL) {
return false;
}
// 将数据赋值给数据域
s->data = x;
// 由于队列必定在队尾插入,所以指针域要设为NULL
s->next = NULL;
// 入队操作
Q.rear->next = s;
// 修改队尾指针
Q.rear = s;
return true;
}
// 出队操作
bool DeQueue(LinkQueue &Q, BiTNode* &x) {
// 空队的情况
if(Q.front == Q.rear) {
return false;
}
// 由于队列出队操作一定是在队头进行,所以以一临时指针指向队头结点(非头结点,是队列第一个结点)
LinkNode *p = Q.front->next;
// 将队头元素赋值给x
x = p->data;
// 令队头指针指向队头元素下一个元素
Q.front->next = p->next;
// 出队为最后一个结点的情况
if(Q.rear == p) {
// 修改尾指针,使其指向头结点,以免之后释放空间时尾指针指向未知地点
// 同时,也表示此队列已是空队列
Q.rear = Q.front;
}
// 释放空间
// 由于之后还需要取分支节点的左右孩子,所以不能在此处释放空间
// free(p);
return true;
}
// 层序遍历
// 即一层一层从左至右访问每个结点,即广度优先遍历
// 此操作需要一个辅助队列,从根结点开始入队,访问此结点后出队,并将此结点的左右孩子入队(如果有)
// 再对每一个入队结点重复上述操作直至遍历完成(队列空)
void LevelOrder(BiTree T) {
LinkQueue Q;
// 初始化辅助队列
InitQueue(Q);
BiTree p;
// 根节点入队
EnQueue(Q, T);
// 队列不空则一直循环
while(!IsEmpty(Q)) {
// 队头结点出队
DeQueue(Q, p);
// 访问出队结点
visit(p);
// 访问出队结点
if(p->lchild != NULL) {
// 左孩子入队
EnQueue(Q, p->lchild);
}
if(p->rchild != NULL) {
// 右孩子入队
EnQueue(Q, p->rchild);
}
// 释放空间
free(p);
}
}
int main() {
BiTree T;
InitTree(T);
InsertLeftTreeNode(T, 'B');
InsertRightTreeNode(T, 'C');
InsertLeftTreeNode(T->lchild, 'D');
InsertRightTreeNode(T->lchild, 'E');
InsertLeftTreeNode(T->rchild, 'F');
InsertRightTreeNode(T->rchild, 'G');
printf("\ntreeDepth:%d\n", treeDepth(T));
printf("LevelOrder\n");
LevelOrder(T);
}