输出展示:
相关文章目录
MFC二叉树可视化绘制 (C++)—— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(基于平衡二叉树实现)
文章目录
前言
该程序由c语言编写,可运行于vs环境下,可轻易转换成C++代码。
一、实现功能
实现操作:结点的插入、删除。
实现输出:输出先序遍历、中序遍历、后序遍历、层序遍历的结果。
二、代码设计
1. 定义平衡二叉树的结点结构
平衡二叉树的结点结构:
// 平衡二叉树的结点结构
typedef struct BiTNode
{
int data;
int bf; // 平衡因子
BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
BiTree 为指向树结点的指针,用于表示树对象。
2. 定义层序遍历链队列相关结构和函数
链队列结构:
/* 链队列结点结构(用于层序遍历) */
typedef struct QNode {
BiTree t; // 结点数据域
struct QNode* next; // 结点指针域
}QNode, * QueuePtr;
/* 链队列结构(用于层序遍历) */
typedef struct {
QueuePtr front; // 队首指针
QueuePtr rear; // 队尾指针
}LinkQueue;
初始化链队列函数、进队函数、出队函数定义:
/* 初始化链队列函数 */
Status InitQueue(LinkQueue& Q)
{
// 创建一个带附加头结点的空链队列
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front) {
return ERROR;
}
Q.front->next = NULL;
return OK;
}
/* 进队函数 */
/* e:插入元素 */
Status EnQueue(LinkQueue& Q, BiTree e)
{
// 将元素e插入到链队列中
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if (!p) {
return ERROR;
}
p->t = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
/* 出队函数 */
/* e:出队元素 */
Status DeQueue(LinkQueue& Q, BiTree& e)
{
// 出队,将出队元素放入e中
QueuePtr p;
if (Q.rear == Q.front) {
return ERROR;
}
p = Q.front->next;
e = p->t;
Q.front->next = p->next;
if (Q.rear == p) {
Q.rear = Q.front;
}
free(p);
return OK;
}
3. 定义结点旋转函数 —— 用于二叉树的结点平衡
LL调整(左单旋)、RR调整(右单旋)、LR调整(先左旋再右旋)、RL调整(先右旋再左旋)。
关于平衡二叉树的旋转问题请参考:平衡二叉树的构造过程图解
// 右旋处理,即LL调整
void R_Rotate(BiTree* p)
{
BiTree L;
L = (*p)->lchild;
(*p)->lchild = L->rchild;
L->rchild = *p;
*p = L;
}
// 左旋处理,即RR调整
void L_Rotate(BiTree* p)
{
BiTree R;
R = (*p)->rchild;
(*p)->rchild = R->lchild;
R->lchild = *p;
*p = R;
}
// 左平衡旋转处理,包括 LL 和 LR 调整
void LeftBalance(BiTree* T)
{
BiTree L, Lr;
L = (*T)->lchild;
switch (L->bf)
{
case 1: // 新结点插入在T的左孩子的左子树上,为LL型,作右旋处理即LL调整
(*T)->bf = L->bf = 0;
R_Rotate(T);
break;
case -1: // 新结点插入在T的左孩子的右子树上,为LR型,作双旋处理
Lr = L->rchild;
switch (Lr->bf)
{
case 1:
(*T)->bf = -1;
L->bf = 0;
break;
case 0:
(*T)->bf = L->bf = 0;
break;
case -1:
(*T)->bf = 0;
L->bf = 1;
break;
}
Lr->bf = 0;
L_Rotate(&(*T)->lchild); // 先对T的左子树进行左旋处理即RR调整
R_Rotate(T); // 再对T进行右旋处理即LL调整
}
}
// 右平衡旋转处理,包括 RR 和 RL 调整
void RightBalance(BiTree* T)
{
BiTree R, Rl;
R = (*T)->rchild;
switch (R->bf)
{
case -1: // 新结点插入在T的右孩子的右子树上,为RR型,作左旋处理即RR调整
(*T)->bf = R->bf = 0;
L_Rotate(T);
break;
case 1: // 新结点插入在T的右孩子的左子树上,为RL型,作双旋处理
Rl = R->lchild;
switch (Rl->bf)
{
case 1:
(*T)->bf = 0;
R->bf = -1;
break;
case 0:
(*T)->bf = R->bf = 0;
break;
case -1:
(*T)->bf = 1;
R->bf = 0;
break;
}
Rl->bf = 0;
R_Rotate(&(*T)->rchild); // 先对T的左子树进行左旋即RR调整
L_Rotate(T); // 再对T进行右旋即LL调整
}
}
4. 定义结点插入函数
// 若在平衡二叉树T中不存在和 e 具有相同数据的结点,则插入数据元素为 e 的新结点,
// 若因插入使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否增加,TRUE表示增加,FALSE表示没有增加
Status InsertAVL(BiTree* T, int e, Status* taller)
{
if (!*T)
{
*T = (BiTree)malloc(sizeof(BiTNode));
if (!*T) {
return ERROR;
}
else {
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = 0;
*taller = TRUE;
}
}
else
{
// 树中已有和e具有相同数据的结点,则不再插入
if (e == (*T)->data)
{
*taller = FALSE;
return FALSE;
}
if (e < (*T)->data) // 继续在T的左子树进行搜索
{
if (!InsertAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
{
return FALSE;
}
// 如果e已插入到T的左子树中,且左子树深度增加
if (*taller)
{
switch ((*T)->bf) // 检查T的平衡因子
{
case 1: // 原本左子树比右子树高,再加上此结点,导致不平衡,需要作LL或LR调整
LeftBalance(T);
*taller = FALSE;
break;
case 0: // 原本左右子树等高,再加上此结点,左子树增高
(*T)->bf = 1;
*taller = TRUE;
break;
case -1: // 原本右子树比左子树高,再加上此结点,左右子树变为等高
(*T)->bf = 0;
*taller = FALSE;
break;
}
}
}
else // 在T的右子树进行搜索
{
if (!InsertAVL(&(*T)->rchild, e, taller))
{
return FALSE;
}
if (*taller)
{
switch ((*T)->bf)
{
case 1: // 原先左子树比右子树高,现在左右子树等高
(*T)->bf = 0;
*taller = FALSE;
break;
case 0: // 原先左右子树等高,现在右子树增高
(*T)->bf = -1;
*taller = TRUE;
break;
case -1: // 原先右子树比左子树高,现在再加上此结点,导致不平衡,需要作 RR 或 RL 调整
RightBalance(T);
*taller = FALSE;
break;
}
}
}
}
return TRUE;
}
5. 定义结点删除函数
// 若在平衡二叉树T中存在和 e 具有相同数据的结点,则删除数据元素为 e 的结点,
// 若因删除使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否改变,TRUE表示改变,FALSE表示没有改变
Status DeleteAVL(BiTree* T, int e, Status* taller)
{
if (!*T) // 树为空,未找到删除结点
{
*taller = FALSE;
return FALSE;
}
else
{
// 树中已有和e具有相同数据的结点,则不再插入
if (e == (*T)->data)
{
if ((*T)->lchild != NULL && (*T)->rchild != NULL) {
}
return TRUE;
}
if (e < (*T)->data) // 继续在T的左子树进行搜索
{
if (!DeleteAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
{
return FALSE;
}
// 如果e已插入到T的左子树中,且左子树深度增加
if (*taller)
{
switch ((*T)->bf) // 检查T的平衡因子
{
case 1: // 原本左子树比右子树高,再删除此结点,变为等高
(*T)->bf = 0;
*taller = TRUE;
break;
case 0: // 原本左右子树等高,再删除此结点,左子树降低
(*T)->bf = -1;
*taller = FALSE;
break;
case -1: // 原本右子树比左子树高,再删除此结点,不平衡
RightBalance(T);
*taller = FALSE;
break;
}
}
}
else // 在T的右子树进行搜索
{
if (!DeleteAVL(&(*T)->rchild, e, taller))
{
return FALSE;
}
if (*taller)
{
switch ((*T)->bf)
{
case 1: // 原先左子树比右子树高
LeftBalance(T);
*taller = FALSE;
break;
case 0: // 原先左右子树等高,右子树变低
(*T)->bf = 1;
*taller = FALSE;
break;
case -1: // 原先右子树比左子树高,变等高
(*T)->bf = 0;
*taller = TRUE;
break;
}
}
}
}
return TRUE;
}
6. 定义二叉树遍历与输出函数
/* 前序遍历 */
void PreOrder(BiTree T)
{
if (T == NULL)
{
return;
}
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
/* 中序遍历 */
void MidOrder(BiTree T)
{
if (T == NULL)
{
return;
}
MidOrder(T->lchild);
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", T->data);
MidOrder(T->rchild);
}
/* 后序遍历 */
void PostOrder(BiTree T)
{
if (T == NULL)
{
return;
}
PostOrder(T->lchild);
PostOrder(T->rchild);
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", T->data);
}
/* 层序遍历 */
void LevelOrder(BiTree root)
{
LinkQueue Q;
BiTree p;
if (root != NULL) {
InitQueue(Q);
EnQueue(Q, root);
while (Q.front != Q.rear) {
DeQueue(Q, p);
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", p->data);
if (p->lchild != NULL) EnQueue(Q, p->lchild);
if (p->rchild != NULL) EnQueue(Q, p->rchild);
}
}
}
void OutputBiTree(BiTree T)
{
if (T != NULL) {
printf("%d", T->data);
if (T->lchild != NULL || T->rchild != NULL) {
printf("(");
OutputBiTree(T->lchild);
if (T->rchild != NULL) {
printf(",");
}
OutputBiTree(T->rchild);
printf(")");
}
}
}
7. main函数测试
int main(void)
{
int i;
int a[10] = { 2,1,0,3,4,5,6,9,8,7 };
BiTree T = NULL;
Status toller;
for (i = 0; i < 10; i++)
{
InsertAVL(&T, a[i], &toller);
}
printf("二叉树括号表示格式输出:\n");
OutputBiTree(T);
printf("\n");
printf("前序遍历\n");
PreOrder(T);
printf("\n");
printf("中序遍历\n");
MidOrder(T);
printf("\n");
printf("后序遍历\n");
PostOrder(T);
printf("\n");
printf("层序遍历\n");
LevelOrder(T);
printf("\n");
return 0;
}
三、运行结果
四、完整代码
#include <stdio.h>
#include <stdlib.h>
//#include <string>
//using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
//string strOrderResult = ""; // 可用于返回多种遍历结果
// 定义平衡二叉树的结点结构
typedef struct BiTNode
{
int data;
int bf; // 平衡因子
BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
typedef int Status;
/* 链队列结点结构(用于层序遍历) */
typedef struct QNode {
BiTree t; // 结点数据域
struct QNode* next; // 结点指针域
}QNode, * QueuePtr;
/* 链队列结构(用于层序遍历) */
typedef struct {
QueuePtr front; // 队首指针
QueuePtr rear; // 队尾指针
}LinkQueue;
/* 初始化链队列函数 */
Status InitQueue(LinkQueue& Q)
{
// 创建一个带附加头结点的空链队列
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front) {
return ERROR;
}
Q.front->next = NULL;
return OK;
}
/* 进队函数 */
/* e:插入元素 */
Status EnQueue(LinkQueue& Q, BiTree e)
{
// 将元素e插入到链队列中
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if (!p) {
return ERROR;
}
p->t = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
/* 出队函数 */
/* e:出队元素 */
Status DeQueue(LinkQueue& Q, BiTree& e)
{
// 出队,将出队元素放入e中
QueuePtr p;
if (Q.rear == Q.front) {
return ERROR;
}
p = Q.front->next;
e = p->t;
Q.front->next = p->next;
if (Q.rear == p) {
Q.rear = Q.front;
}
free(p);
return OK;
}
// 右旋处理,即LL调整
void R_Rotate(BiTree* p)
{
BiTree L;
L = (*p)->lchild;
(*p)->lchild = L->rchild;
L->rchild = *p;
*p = L;
}
// 左旋处理,即RR调整
void L_Rotate(BiTree* p)
{
BiTree R;
R = (*p)->rchild;
(*p)->rchild = R->lchild;
R->lchild = *p;
*p = R;
}
// 左平衡旋转处理,包括 LL 和 LR 调整
void LeftBalance(BiTree* T)
{
BiTree L, Lr;
L = (*T)->lchild;
switch (L->bf)
{
case 1: // 新结点插入在T的左孩子的左子树上,为LL型,作右旋处理即LL调整
(*T)->bf = L->bf = 0;
R_Rotate(T);
break;
case -1: // 新结点插入在T的左孩子的右子树上,为LR型,作双旋处理
Lr = L->rchild;
switch (Lr->bf)
{
case 1:
(*T)->bf = -1;
L->bf = 0;
break;
case 0:
(*T)->bf = L->bf = 0;
break;
case -1:
(*T)->bf = 0;
L->bf = 1;
break;
}
Lr->bf = 0;
L_Rotate(&(*T)->lchild); // 先对T的左子树进行左旋处理即RR调整
R_Rotate(T); // 再对T进行右旋处理即LL调整
}
}
// 右平衡旋转处理,包括 RR 和 RL 调整
void RightBalance(BiTree* T)
{
BiTree R, Rl;
R = (*T)->rchild;
switch (R->bf)
{
case -1: // 新结点插入在T的右孩子的右子树上,为RR型,作左旋处理即RR调整
(*T)->bf = R->bf = 0;
L_Rotate(T);
break;
case 1: // 新结点插入在T的右孩子的左子树上,为RL型,作双旋处理
Rl = R->lchild;
switch (Rl->bf)
{
case 1:
(*T)->bf = 0;
R->bf = -1;
break;
case 0:
(*T)->bf = R->bf = 0;
break;
case -1:
(*T)->bf = 1;
R->bf = 0;
break;
}
Rl->bf = 0;
R_Rotate(&(*T)->rchild); // 先对T的左子树进行左旋即RR调整
L_Rotate(T); // 再对T进行右旋即LL调整
}
}
// 若在平衡二叉树T中不存在和 e 具有相同数据的结点,则插入数据元素为 e 的新结点,
// 若因插入使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否增加,TRUE表示增加,FALSE表示没有增加
Status InsertAVL(BiTree* T, int e, Status* taller)
{
if (!*T)
{
*T = (BiTree)malloc(sizeof(BiTNode));
if (!*T) {
return ERROR;
}
else {
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = 0;
*taller = TRUE;
}
}
else
{
// 树中已有和e具有相同数据的结点,则不再插入
if (e == (*T)->data)
{
*taller = FALSE;
return FALSE;
}
if (e < (*T)->data) // 继续在T的左子树进行搜索
{
if (!InsertAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
{
return FALSE;
}
// 如果e已插入到T的左子树中,且左子树深度增加
if (*taller)
{
switch ((*T)->bf) // 检查T的平衡因子
{
case 1: // 原本左子树比右子树高,再加上此结点,导致不平衡,需要作LL或LR调整
LeftBalance(T);
*taller = FALSE;
break;
case 0: // 原本左右子树等高,再加上此结点,左子树增高
(*T)->bf = 1;
*taller = TRUE;
break;
case -1: // 原本右子树比左子树高,再加上此结点,左右子树变为等高
(*T)->bf = 0;
*taller = FALSE;
break;
}
}
}
else // 在T的右子树进行搜索
{
if (!InsertAVL(&(*T)->rchild, e, taller))
{
return FALSE;
}
if (*taller)
{
switch ((*T)->bf)
{
case 1: // 原先左子树比右子树高,现在左右子树等高
(*T)->bf = 0;
*taller = FALSE;
break;
case 0: // 原先左右子树等高,现在右子树增高
(*T)->bf = -1;
*taller = TRUE;
break;
case -1: // 原先右子树比左子树高,现在再加上此结点,导致不平衡,需要作 RR 或 RL 调整
RightBalance(T);
*taller = FALSE;
break;
}
}
}
}
return TRUE;
}
// 若在平衡二叉树T中存在和 e 具有相同数据的结点,则删除数据元素为 e 的结点,
// 若因删除使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否改变,TRUE表示改变,FALSE表示没有改变
Status DeleteAVL(BiTree* T, int e, Status* taller)
{
if (!*T) // 树为空,未找到删除结点
{
*taller = FALSE;
return FALSE;
}
else
{
// 树中已有和e具有相同数据的结点,则不再插入
if (e == (*T)->data)
{
if ((*T)->lchild != NULL && (*T)->rchild != NULL) {
}
return TRUE;
}
if (e < (*T)->data) // 继续在T的左子树进行搜索
{
if (!DeleteAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
{
return FALSE;
}
// 如果e已插入到T的左子树中,且左子树深度增加
if (*taller)
{
switch ((*T)->bf) // 检查T的平衡因子
{
case 1: // 原本左子树比右子树高,再删除此结点,变为等高
(*T)->bf = 0;
*taller = TRUE;
break;
case 0: // 原本左右子树等高,再删除此结点,左子树降低
(*T)->bf = -1;
*taller = FALSE;
break;
case -1: // 原本右子树比左子树高,再删除此结点,不平衡
RightBalance(T);
*taller = FALSE;
break;
}
}
}
else // 在T的右子树进行搜索
{
if (!DeleteAVL(&(*T)->rchild, e, taller))
{
return FALSE;
}
if (*taller)
{
switch ((*T)->bf)
{
case 1: // 原先左子树比右子树高
LeftBalance(T);
*taller = FALSE;
break;
case 0: // 原先左右子树等高,右子树变低
(*T)->bf = 1;
*taller = FALSE;
break;
case -1: // 原先右子树比左子树高,变等高
(*T)->bf = 0;
*taller = TRUE;
break;
}
}
}
}
return TRUE;
}
/* 前序遍历 */
void PreOrder(BiTree T)
{
if (T == NULL)
{
return;
}
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", T->data);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
/* 中序遍历 */
void MidOrder(BiTree T)
{
if (T == NULL)
{
return;
}
MidOrder(T->lchild);
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", T->data);
MidOrder(T->rchild);
}
/* 后序遍历 */
void PostOrder(BiTree T)
{
if (T == NULL)
{
return;
}
PostOrder(T->lchild);
PostOrder(T->rchild);
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", T->data);
}
/* 层序遍历 */
void LevelOrder(BiTree root)
{
LinkQueue Q;
BiTree p;
if (root != NULL) {
InitQueue(Q);
EnQueue(Q, root);
while (Q.front != Q.rear) {
DeQueue(Q, p);
//strOrderResult = strOrderResult + to_string(T->data) + " ";
printf("%d ", p->data);
if (p->lchild != NULL) EnQueue(Q, p->lchild);
if (p->rchild != NULL) EnQueue(Q, p->rchild);
}
}
}
void OutputBiTree(BiTree T)
{
if (T != NULL) {
printf("%d", T->data);
if (T->lchild != NULL || T->rchild != NULL) {
printf("(");
OutputBiTree(T->lchild);
if (T->rchild != NULL) {
printf(",");
}
OutputBiTree(T->rchild);
printf(")");
}
}
}
// 对于实例,我们可以这样创建平衡二叉树
int main(void)
{
int i;
int a[10] = { 2,1,0,3,4,5,6,9,8,7 };
BiTree T = NULL;
Status toller;
for (i = 0; i < 10; i++)
{
InsertAVL(&T, a[i], &toller);
}
printf("二叉树括号表示格式输出:\n");
OutputBiTree(T);
printf("\n");
printf("前序遍历\n");
PreOrder(T);
printf("\n");
printf("中序遍历\n");
MidOrder(T);
printf("\n");
printf("后序遍历\n");
PostOrder(T);
printf("\n");
printf("层序遍历\n");
LevelOrder(T);
printf("\n");
return 0;
}