实现二叉树的遍历及其动画演示
什么是二叉树
二叉树是一种常见的数据结构,它由节点和边组成,每个节点最多有两个子节点。在二叉树中,有三种常见的遍历方式:前序遍历、中序遍历和后序遍历。此外,还有一种层次遍历方式。下面分别介绍这些遍历方式:
给大家推荐一个国内免费使用的chatGpt:Sider -- ChatGPT Sidebar, GPT-4, Draw & Web access
1.前序遍历
前序遍历是指从根节点开始,先遍历根节点,然后遍历左子树,最后遍历右子树。具体步骤如下:
(1)访问根节点;
(2)递归遍历左子树;
(3)递归遍历右子树。
2.中序遍历
中序遍历是指从根节点开始,先遍历左子树,然后遍历根节点,最后遍历右子树。具体步骤如下:
(1)递归遍历左子树;
(2)访问根节点;
(3)递归遍历右子树。
3.后序遍历
后序遍历是指从根节点开始,先遍历左子树,然后遍历右子树,最后遍历根节点。具体步骤如下:
(1)递归遍历左子树;
(2)递归遍历右子树;
(3)访问根节点。
4.层次遍历
层次遍历是指从根节点开始,按照层次顺序依次遍历每个节点。具体步骤如下:
(1)将根节点入队;
(2)从队列中取出一个节点,访问它;
(3)将该节点的左子节点和右子节点入队;
(4)重复步骤2和3,直到队列为空。
以上是四种常见的二叉树遍历方式。在实际应用中,根据不同的问题和需求,可以选择不同的遍历方式。例如,前序遍历可以用于复制二叉树,中序遍历可以用于排序,后序遍历可以用于计算表达式的值,层次遍历可以用于搜索最短路径等。
使用easy-X实现动画的演示。
Easy-X是一款基于C++的图形库,可以用来实现简单的图形绘制和动画效果。使用Easy-X可以方便地创建窗口、绘制图形、播放音频等。下面是使用Easy-X实现动画的基本步骤:
-
创建窗口 使用initgraph函数创建一个窗口,设置窗口的大小和标题等参数。
-
绘制图形 使用各种绘图函数(如line、circle、rectangle等)绘制图形,可以设置颜色、线条粗细等参数。
-
实现动画效果 使用循环结构(如while或for)控制动画的帧数和时间间隔,每帧更新图形的位置、大小等参数,实现动画效果。
-
清除图形 在每次更新图形之前,使用cleardevice函数清除上一帧的图形,避免图形叠加。
-
关闭窗口 使用closegraph函数关闭窗口,释放资源。
以上是使用Easy-X实现动画的基本步骤,具体实现方式可以参考Easy-X的官方文档和示例代码。
全部代码如下
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Stdlib.h>
#include<String.h>
#include<graphics.h>
#include<easyx.h>
#define ERROR 0
#define OK 1
#define Stack_Init_size 100
//二叉树
typedef int status;
typedef char BTrElemType;
typedef struct BiNode {
//数据
BTrElemType data;
//左右孩子
struct BiNode* lchild;
struct BiNode* rchild;
}BiNode, * BiTree;
//创建二叉树
BiTree createLinkTree();
//菜单
void menu();
//一:递归 - 前 - 中 - 后 - 层序 - 遍历
//1.1递归前序遍历
void PreOrderTraverse(BiTree T) {
if (T != NULL) {
printf("%3c", T->data);
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
//1.2·中序递归遍历
void MidOrderTraverse(BiTree T) {
if (T != NULL) {
MidOrderTraverse(T->lchild);
printf("%3c", T->data);
MidOrderTraverse(T->rchild);
}
}
//1.3·后序递归遍历
void PostOrderTraverse(BiTree T) {
if (T != NULL) {
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%3c", T->data);
}
}
//1.4.递归层序遍历
//层序递归遍历需要先求出二叉树的高度,也就是二叉树的深度
//目的是为了进行递归遍历时判断何时才应该打印输出该节点。
// 求二叉树深度,(求二叉树高度)
//递归遍历求二叉树深度,本质就是求得每个结点的深度然后再返回给上一个结点
//直到返回到树的根结点
int treeHigh(BiTree T) {
if (T == NULL) {
return 0;
}
else {
int lhigh = treeHigh(T->lchild);//左子树高度
int rhigh = treeHigh(T->rchild);//右子树高度
int maxhigh = lhigh > rhigh ? lhigh + 1 : rhigh + 1;//计算该结点处的深度
return maxhigh; //返回该结点处的高度
}
}
//递归层序打印输出
void levelPrint(BiTree T, int level) {
if (T != NULL) {
if (level == 1) {
//到达本层即打印输出
printf("%3c", T->data);
}
else {
//递归直到本层
levelPrint(T->lchild, level - 1);
levelPrint(T->rchild, level - 1);
}
}
}
//递归层序遍历
void levelOrderTravel(BiTree T) {
int i = 0;
if (T != NULL) {
int h = treeHigh(T);
for (i = 1; i <= h; i++) {
levelPrint(T, i);
}
}
}
//二:非递归-前-中-后-层序-遍历
//非递归主要涉及栈的操作,就是元素入栈出栈
//栈的结构类型定义:
typedef struct SqStack {
BiTree* base;//栈底指针
BiTree* top;//栈顶指针
int stacksize;//栈空间大小
}SqStack;
//2.1栈的初始化
void InitStack(SqStack* s) {
s->base = (BiTree*)malloc(Stack_Init_size * sizeof(BiNode));
if (s->base == NULL) {
exit(0);
}
else {
s->top = s->base;
s->stacksize = Stack_Init_size;
}
}
//2.2弹栈,栈尾元素出栈
void Pop(SqStack* s, BiTree* q) {
if (s->base == s->top) {
printf("栈空,无法进行出栈操作\n");
}
else {
s->top--;
*q = *s->top;
}
}
//2.3入栈
#define StackIncrement 2
void Push(SqStack* s, BiTree p) {
//满栈就再为其增加空间
if (s->top - s->base == s->stacksize) {
s->base = (BiTree*)realloc(s->base, (s->stacksize + StackIncrement) * sizeof(BiNode));
if (!s->base) {
printf("内存增加失败\n");
exit(0);
}
else {
s->top = s->base + s->stacksize;
s->stacksize += StackIncrement;
}
}
*s->top++ = p;//入栈
}
//2.4判断栈是否为空
status StackEmpty(SqStack* s) {
if (s->top == s->base) {
return OK;
}
else {
return ERROR;
}
}
//2.5取栈顶元素
void GetTop(SqStack* s, BiTree* q) {
if (s->base == s->top) {
printf("栈为空,无法进行取栈顶元素的操作\n");
}
else {
BiTree* temp = s->top;
*q = *(--s->top);
s->top = temp;
//取出栈顶元素,但不可改变原本栈顶指针指向的位置
}
}
//2.6前序非递归遍历
void PreTrave(BiTree T) {
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
InitStack(s);
BiTree p = T, q = NULL;
while (p || !StackEmpty(s)) {
if (p != NULL) {
Push(s, p);
printf("%3c", p->data);
p = p->lchild;
}
else {
Pop(s, &q);
p = q->rchild;
}
}
}
//2.7中序非递归遍历
void MidTrave(BiTree T) {
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
InitStack(s);
BiTree p = T, q = NULL;
while (p || !StackEmpty(s)) {
if (p != NULL) {
Push(s, p);
p = p->lchild;
}
else {
Pop(s, &q);
printf("%3c", q->data);
p = q->rchild;
}
}
}
//2.8后序非递归
void PostTrave(BiTree T) {
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
InitStack(s);
BiTree p = T, q = NULL;
BiTree flagNode = NULL;//标志位结点
BiTree top = NULL;//栈顶元素结点
while (p || !StackEmpty(s)) {
if (p != NULL) {
Push(s, p);//左子树入栈
p = p->lchild;
}
else {
GetTop(s, &top);
//进入这个else即可知该结点左孩子为空,若右孩子也为空,则该结点为一个叶子结点,可直接访问
//若右孩子结点为已访问过的,则该节点为一可访问的父节点
if (top->rchild == NULL || top->rchild == flagNode) {
printf("%3c", top->data);
Pop(s, &q);//弹栈
flagNode = top;
p = NULL;
}
else {
//存在未访问的右节点,右节点入栈
p = top->rchild;
}
}
}
}
//2.9层序非递归
//主要涉及到队列的操作,结点的出队,入队.
//链式队列,存放二叉树结点
typedef struct QNode {
BiNode* data;
struct QNode* next;
}Qnode, * QueuePtr;
//队列头指针、尾指针
typedef struct LinkQueue {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
//2.9队列初始化,创建一个空队列
void initQueue(LinkQueue& Q) {
Q.front = (QueuePtr)malloc(sizeof(Qnode));
Q.rear = Q.front;
Q.front->next = NULL;
}
//2.10入队
void EnQueue(LinkQueue& Q, BiTree e) {
QNode* p;
p = (QueuePtr)malloc(sizeof(Qnode));
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
}
//2.11出队
BiTree DeQueue(LinkQueue& Q, BiTree* e) {
if (Q.front == Q.rear) {
printf("队列为空,无法进行出队操作\n");
return NULL;
}
else {
QueuePtr p = Q.front->next;
*e = p->data;
Q.front->next = p->next;
if (p == Q.rear) {
Q.front = Q.rear;
}
return *e;
}
}
//2.12判断队列是否为空
status QueueEmpty(LinkQueue Q) {
if (Q.front == Q.rear) {
return OK;
}
else {
return ERROR;
}
}
//2.13非递归层序遍历
void LevelTrave(BiTree T) {
BiTree p = T, temp = NULL;
LinkQueue Q;
initQueue(Q);
EnQueue(Q, p);//根节点入队
while (!QueueEmpty(Q)) {
temp = DeQueue(Q, &p);
printf("%3c", temp->data);
if (temp->lchild != NULL) {
EnQueue(Q, temp->lchild);
}
if (temp->rchild != NULL) {
EnQueue(Q, temp->rchild);
}
}
}
//三.线索二叉树
typedef char BThrElemType;
typedef struct BiThrNode {
BThrElemType data;//数据
struct BiThrNode* lchild;//左孩子
struct BiThrNode* rchild;//右孩子
int ltag, rtag;//左右标志域
} BiThrNode, * BiThrTree;
BiThrNode* preNode; // 当前访问节点的前驱(前继结点)
//3.1先序创建线索二叉树
BiThrTree createBiThreadTree() {
BiThrNode* T;
char Thdata;
scanf("%c", &Thdata);
if (Thdata == '#') {
return NULL;
}
else {
T = (BiThrNode*)malloc(sizeof(BiThrNode));
T->data = Thdata;
//左子树
T->lchild = createBiThreadTree();
//右子树
T->rchild = createBiThreadTree();
return T;
}
}
// 3.2中序遍历二叉树线索化
void MakingMidThrTree(BiThrTree T) {
if (T != NULL) {
MakingMidThrTree(T->lchild);//线索化左子树
if (T->lchild != NULL) {
T->ltag = 0;//存在左孩子
}
else {
T->ltag = 1;//不存在左孩子
T->lchild = preNode;//左孩子指针域指向前驱结点
}
if (preNode->rchild != NULL) {
preNode->rtag = 0;//存在右孩子
}
else {
preNode->rtag = 1;//不存在右孩子
preNode->rchild = T;//右孩子指针域指向后继结点
}
preNode = T;
MakingMidThrTree(T->rchild);//线索化右子树
}
}
// 3.3中序线索二叉树处理
BiThrTree CreateMidThrTree(BiThrTree T) {
//中序线索二叉树头结点非二叉树的根结点
BiThrNode* Thead;
Thead = (BiThrNode*)malloc(sizeof(BiThrNode));
//头结点
Thead->ltag = 0;
Thead->rtag = 1;
Thead->rchild = Thead;//右孩子指针域暂时指向自身
Thead->lchild = T;
preNode = Thead;
MakingMidThrTree(T);将该二叉树线索化
//中序遍历尾结点处理
preNode->rchild = Thead;
preNode->rtag = 1;
Thead->rchild = preNode;
return Thead;
}
// 3.4遍历中序线索二叉树
void midThrOrderTrave(BiThrTree ThrHead) {
//线索二叉树头节点非根节点,头节点左孩子指向二叉树的根节点
BiThrNode* p = ThrHead->lchild;
while (p != ThrHead) {
while (p->ltag == 0) {
p = p->lchild;//找到根节点的最左子树的最左的结点
}
printf("%3c", p->data);
while (p->rtag == 1 && p->rchild != ThrHead) {
p = p->rchild;
printf("%3c", p->data);
}
p = p->rchild;
}
}
//四.二叉树遍历动画演示(EasyX)
//4.1 绘制二叉树
void drawTree(FILE* fp1, BiTree t, int x, int y, int r, int x_Extend, int y_Extend) {
//printf("%4d%4d%4d%4d\n", x, y, x_Extend, y_Extend);
if (t->lchild != NULL) {//直到左子树的左叶子结点
line(x, y, x - x_Extend, y + y_Extend);
drawTree(fp1, t->lchild, x - x_Extend, y + y_Extend, r, x_Extend / 2, y_Extend * 1.2);
}
circle(x, y, r);
outtextxy(x, y, t->data);
fprintf(fp1, "%d %d %c\n", x, y, t->data);//记录每个结点的坐标信息
if (t->rchild != NULL) {
line(x, y, x + x_Extend, y + y_Extend);
drawTree(fp1, t->rchild, x + x_Extend, y + y_Extend, r, x_Extend / 2, y_Extend * 1.2);
}
}
//4.2 前序遍历动画演示
void PreEasyXTrave(BiTree T, int x, int y, int r) {
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
InitStack(s);
BiTree p = T, q = NULL;
char ch, tc;
int i = 1;
FILE* fp = fopen("test.txt", "r");
int x2 = 20, y2 = 800;
while (p || !StackEmpty(s)) {
if (p != NULL) {
Push(s, p);
//printf("%3c", p->data);
while (fscanf(fp, "%d %d %c", &x, &y, &ch) == 3) {
if (ch == p->data) {
circle(x, y + 60, 20);
if (i < 10) {
tc = i + '0';
outtextxy(x, y + 60, tc);
}
else {
tc = i / 10 + '0';
outtextxy(x, y + 60, tc);
tc = i % 10 + '0';
outtextxy(x + 5, y + 60, tc);
}
i++;
circle(x2, y2, 20);
outtextxy(x2, y2, p->data);
x2 += 60;
_gettch();
break;
}
}
rewind(fp);
p = p->lchild;
}
else {
Pop(s, &q);
p = q->rchild;
}
}
fclose(fp);
}
//4.3中序遍历
void MidEasyXTrave(BiTree T) {
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
InitStack(s);
BiTree p = T, q = NULL;
char ch, tc;
int x = 0, y = 0;
int x2 = 20, y2 = 800;
int i = 1;
FILE* fp = fopen("test.txt", "r");
while (p || !StackEmpty(s)) {
if (p != NULL) {
Push(s, p);
p = p->lchild;
}
else {
Pop(s, &q);
//printf("%3c", q->data);
while (fscanf(fp, "%d %d %c", &x, &y, &ch) == 3) {
if (ch == q->data) {
circle(x, y + 60, 20);
if (i < 10) {
tc = i + '0';
outtextxy(x, y + 60, tc);
}
else {
tc = i / 10 + '0';
outtextxy(x, y + 60, tc);
tc = i % 10 + '0';
outtextxy(x + 5, y + 60, tc);
}
i++;
circle(x2, y2, 20);
outtextxy(x2, y2, q->data);
x2 += 60;
_gettch();
break;
}
}
rewind(fp);
p = q->rchild;
}
}
fclose(fp);
}
//4.4后序遍历
void PostEasyXTrave(BiTree T) {
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
InitStack(s);
BiTree p = T, q = NULL;
BiTree flagNode = NULL;//标志位结点
BiTree top = NULL;//栈顶元素结点
char ch, tc;
int x = 0, y = 0;
int x2 = 20, y2 = 800;
int i = 1;
FILE* fp = fopen("test.txt", "r");
while (p || !StackEmpty(s)) {
if (p != NULL) {
Push(s, p);//左子树入栈
p = p->lchild;
}
else {
GetTop(s, &top);
//进入这个else即可知该结点左孩子为空,若右孩子也为空,则该结点为一个叶子结点,可直接访问
//若右孩子结点为已访问过的,则该节点为一可访问的父节点
if (top->rchild == NULL || top->rchild == flagNode) {
//printf("%3c", top->data);
while (fscanf(fp, "%d %d %c", &x, &y, &ch) == 3) {
if (ch == top->data) {
circle(x, y + 60, 20);
if (i < 10) {
tc = i + '0';
outtextxy(x, y + 60, tc);
}
else {
tc = i / 10 + '0';
outtextxy(x, y + 60, tc);
tc = i % 10 + '0';
outtextxy(x + 5, y + 60, tc);
}
i++;
circle(x2, y2, 20);
outtextxy(x2, y2, top->data);
x2 += 60;
_gettch();
break;
}
}
rewind(fp);
Pop(s, &q);//弹栈
flagNode = top;
p = NULL;
}
else {
//存在未访问的右节点,右节点入栈
p = top->rchild;
}
}
}
fclose(fp);
}
//4.5层序遍历
void LevelEasyXTrave(BiTree T) {
BiTree p = T, temp = NULL;
LinkQueue Q;
initQueue(Q);
EnQueue(Q, p);//根节点入队
char ch, tc;
int x = 0, y = 0;
int x2 = 20, y2 = 800;
int i = 1;
FILE* fp = fopen("test.txt", "r");
while (!QueueEmpty(Q)) {
temp = DeQueue(Q, &p);
//printf("%3c", temp->data);
while (fscanf(fp, "%d %d %c", &x, &y, &ch) == 3) {
if (ch == temp->data) {
circle(x, y + 60, 20);
if (i < 10) {
tc = i + '0';
outtextxy(x, y + 60, tc);
}
else {
tc = i / 10 + '0';
outtextxy(x, y + 60, tc);
tc = i % 10 + '0';
outtextxy(x + 5, y + 60, tc);
}
i++;
circle(x2, y2, 20);
outtextxy(x2, y2, temp->data);
x2 += 60;
_gettch();
break;
}
}
rewind(fp);
if (temp->lchild != NULL) {
EnQueue(Q, temp->lchild);
}
if (temp->rchild != NULL) {
EnQueue(Q, temp->rchild);
}
}
fclose(fp);
}
void settree(BiTree t3) {
FILE* fp = fopen("test.txt", "w");
if (fp == NULL) {
printf("文件打开失败,请重新打开\n");
exit(0);
}
initgraph(1000, 1000, SHOWCONSOLE); //初始化窗口
setbkcolor(LIGHTBLUE); cleardevice();//设置背景颜色
drawTree(fp, t3, 500, 30, 30, 150, 150);
fclose(fp);
}
int main() {
BiTree t1 = NULL;
BiThrTree t2 = NULL;
BiThrNode* BiThrHead = NULL;
BiTree t3 = NULL;
FILE* fp;
int choose = 0;
while (1) {
menu();
scanf("%d", &choose);
getchar();
if (choose == 20) break;
switch (choose) {
case 1:
printf("前序递归遍历 : ");
PreOrderTraverse(t1);//前序递归遍历
printf("\n\n\n");
break;
case 2:
printf("\n中序递归遍历 : ");
MidOrderTraverse(t1);//中序递归遍历
printf("\n\n\n");
break;
case 3:
printf("\n后序递归遍历 : ");
PostOrderTraverse(t1);//后序递归遍历
printf("\n\n\n");
break;
case 4:
printf("递归层序遍历:");
levelOrderTravel(t1);
printf("\n\n\n");
break;
case 5:
printf("\n前序非递归遍历:");
PreTrave(t1);
printf("\n\n\n");
break;
case 6:
printf("\n中序非递归遍历:");
MidTrave(t1);
printf("\n\n\n");
break;
case 7:
printf("\n后序非递归遍历:");
PostTrave(t1);
printf("\n\n\n");
break;
case 8:
printf("\n非递归层序遍历:");
LevelTrave(t1);
printf("\n\n\n");
break;
case 9:
printf("\n中序线索化二叉树遍历:");
midThrOrderTrave(BiThrHead);//中序遍历线索二叉树
printf("\n\n\n");
break;
case 10:
printf("请输入创建树的结点");
printf("\t(备注:输入“#”表示结束当前子树的创建)\n输入:");
t1 = createLinkTree();
getchar();
printf("二叉树创建成功,请继续选择操作\n\n\n");
break;
case 11:
printf("为创建的线索化二叉树输入的结点");
printf("\t(备注:输入“#”表示结束当前子树的创建)\n输入:");
t2 = createBiThreadTree();//二叉树创建
getchar();
BiThrHead = CreateMidThrTree(t2);//创建中序线索二叉树
printf("线索二叉树创建成功,请继续选择操作\n\n\n");
break;
case 13:
printf("EasyX创建二叉树\n输入:");
t3 = createLinkTree();
printf("创建成功!\n");
break;
case 14:
printf("前序遍历动画演示\n");
settree(t3);
PreEasyXTrave(t3, 30, 500, 30);
printf("任意键完成动画演示\n");
getchar();//防止程序闪退
closegraph();//关闭窗口
printf("前序遍历动画演示完成\n");
break;
case 15:
printf("中序遍历动画演示\n");
settree(t3);
MidEasyXTrave(t3);
printf("任意键完成动画演示\n");
getchar();//防止程序闪退
closegraph();//关闭窗口
printf("前序遍历动画演示完成\n");
break;
case 16:
printf("后序遍历动画演示\n");
settree(t3);
PostEasyXTrave(t3);
printf("任意键完成动画演示\n");
getchar();//防止程序闪退
closegraph();//关闭窗口
printf("后序遍历动画演示完成\n");
break;
case 17:
printf("层序遍历动画演示\n");
settree(t3);
LevelEasyXTrave(t3);
printf("任意键完成动画演示\n");
getchar();//防止程序闪退
closegraph();//关闭窗口
printf("层序遍历动画演示完成\n");
break;
case 20:
break;
default:
printf("\n\n*************需要退出请输入:20 *************\n\n");
break;
}
}
}
//测试数据
//ABDH##I##EJ##K##CFL##M##GN##O##
//ABD##E##CF###
//ABD##E##CF##G##
//AB##C##
//
//创建二叉树
BiTree createLinkTree() {
BiNode* t = NULL;
BTrElemType Tdata;
scanf("%c", &Tdata);
if (Tdata == '#') {
return NULL;
}
else {
t = (BiNode*)malloc(sizeof(BiNode));
t->data = Tdata;
t->lchild = createLinkTree();//左子树
t->rchild = createLinkTree();//右子树
return t;
}
}
//菜单
void menu() {
printf("**********************菜单选项*************************************\n");
printf("******** 一:递归 *************\n");
printf("*******************************************************************\n");
printf("********1:前序递归遍历 2:中序递归遍历 *************\n");
printf("********3:后序递归遍历 4:层序递归遍历 *************\n");
printf("*******************************************************************\n");
printf("******** 二:非递归 *************\n");
printf("*******************************************************************\n");
printf("********5:前序非递归遍历 6:中序非递归遍历 *************\n");
printf("********7:后序非递归遍历 8:层序递非归遍历 *************\n");
printf("*******************************************************************\n");
printf("******** 三:线索二叉树 *************\n");
printf("*******************************************************************\n");
printf("********9:中序线索化二叉树的遍历 *************\n");
printf("*******************************************************************\n");
printf("******** 四:创建二叉树 *************\n");
printf("*******************************************************************\n");
printf("********10:二叉树创建 11:线索二叉树创建 *************\n");
printf("*******************************************************************\n");
printf("******** 五:二叉树前-中-后-层序遍历动画演示*************\n");
printf("*******************************************************************\n");
printf("********13:使用EasyX绘制二叉树 *************\n");
printf("********14:前序遍历 15:中序遍历 *************\n");
printf("********16:后序遍历 17:层序遍历 *************\n");
printf("*******************************************************************\n");
printf("********20:退出 *************\n");
printf("*******************************************************************\n");
printf("请输入:");
}