数据结构与算法分析实验6 [进阶]构建二叉树并进行图形化遍历(EasyX)

1、上机名称

实现二叉树的创建和图形化遍历

2、上机要求

  1. 用三叉链表储存一个二叉树
  2. 对二叉树进行图形化遍历(前中后层)

3、上机环境

Microsoft visual studio 2022 Windows11 64位操作系统
EasyX插件库装载,参见:easyx官网安装教程

4、程序清单

4.1 Mytree.h头文件

头文件引入、宏定义、三叉链表节点、绘图所用的位置信息

#pragma once
#pragma warning (disable:4996)
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<math.h>
#include<graphics.h>
#include <conio.h>
#define ConsoleX 640	//控制台宽
#define ConsoleY 960	//控制台高
#define offset 3		//绘图偏移量
#define rx 30			//打印字符长
#define ry 40			//打印字符宽
//有数据修改能传引用传引用(能传指针传指针),可以解决99%的问题。
#define maxsize 50			//假设容器里面最大不会超过50个数据
typedef int Level;
typedef char Data;			//数据元素用字符型抽象
typedef struct POS{
	int x;
	int y;
}Pos;
typedef struct TNode {
	int id;					//标识创建顺序的ID
	Data data;				//数据元素
	Level level;			//节点的层数标记
	Pos pos;				//通过freshposx freshposy分配的位置信息,需要先分配level
	struct TNode* parent;	//双亲节点
	struct TNode* lcd;		//左子树
	struct TNode* rcd;		//右子树
}Tnode, * pTnode;			//一个三叉链表节点

//两个小定义用起来舒服点
typedef Tnode TreeData;				
typedef pTnode pTreeData;

创建一个容器,初始化树使用、遍历使用

//容器的创建
typedef struct Vector {		//一个多功能容器,需要装的是树的节点指针
	pTreeData data[maxsize];
	int length;
}vector;
void init_vector(vector& v);				//初始化
void push_back(vector& v,pTnode tree);		//尾插法
void push_front(vector& v, pTnode tree);	//头插法
Tnode pop_back(vector& v);					//尾部取元素
Tnode pop_front(vector& v);					//头部取元素
int getlen(vector v);						//长度信息

二叉树基本操作,与基本实验相同,实现中略有不同。

//二叉树操作
vector TreeCreate(/*pTnode* parent,*/pTnode& tree);//创建二叉树	同时返回容器装好每个节点的位置,便于输出
void TreeOutput(vector v);		//输出二叉树	我采用的是按照创建序号索引进行输出,打印所有具体信息
int gethigh(pTnode tree);		//返回二叉树高度采用递归策略
void First_Order_View(vector v);	//先序遍历二叉树,由于创建时采用先序法创建,所以ID顺序即为先序遍历顺序
void Second_Order_View(pTnode tree);//中序遍历二叉树,采用递归策略。
void Third_Order_View(pTnode tree);	//后序遍历二叉树,采用递归策略。
void Level_Order_View(pTnode tree); //层序遍历,需要用到队列,拿上面的容器当队列。
void TreeDestory(vector& v);		//可以通过遍历销毁树,也通过对容器的操作销毁树,我采取后一种方法,因为容器也需要销毁。

freshlevel:在树建立之后,分配树的层级信息,由树根开始从1向大分配,需要传入树的总高度,失败返回0
freshposx:在层级信息完善后,分配树的x坐标信息。
freshpoxy:在层级信息完善后,分配树的y坐标信息。
makestatus:上三者的综合调用,需要将create调用形成的vector传入,调用此函数来完成位置信息的更新即可。

//绘图信息配置
int freshlevel(int treehigh,pTnode tree);	
int freshposx(pTnode tree);					
int freshposy(int treehigh,pTnode tree);	
void makestatus(vector treeinfo);			

绘图操作,包括输出数据节点和输出节点的连线

//绘图操作
void putdata(pTnode tree);
void putlink(pTnode tree);

4.2 Mytree.cpp实现文件

两个全局变量,用法已给出注释

#include "Mytree.h"
int ID = 0;					//用于统计创建树的有效大小,标记ID
vector TreeInfo = {};		//创建形成的链表,通过线性存储方式储存树节点位置

容器功能的实现,与基本实验无区别

//容器功能实现
void init_vector(vector& v){
	v.length = 0;
	for (int i = 0; i < maxsize; i++) {
		v.data[i] = NULL;
	}
}

void push_back(vector& v, pTnode tree){
	v.data[v.length] = tree;
	v.length++;
}

void push_front(vector& v, pTnode tree){
	for (int i = v.length; i > 0; i--) {
		v.data[i] = v.data[i - 1];
	}
	v.data[0] = tree;
	v.length++;
}

Tnode pop_back(vector& v)
{	
	if (v.length > 0) {
		v.length--;
		return *v.data[v.length];
	}
	else exit(-1);
}

Tnode pop_front(vector& v){
	if (v.length > 0) {
		Tnode ret = *v.data[0];
		for (int i = 0; i < v.length - 1; i++) {
			v.data[i] = v.data[i + 1];
		}
		v.length--;
		return ret;
	}
	else exit(-1);
}

int getlen(vector v)
{
	return v.length;
}

树的相关代码实现,主要改动在于创建时考虑父节点的创建、遍历时输出图形的逻辑、暂停

//树的功能操作
vector TreeCreate(/*pTnode* parent,*/pTnode& tree){
	Data data;		//用具接受存储数据输入
	char key;		//用于接收用户提示信息输入
	//初始化tree
	tree = (pTnode)malloc(sizeof(TNode));
	/*tree->parent = *parent;*/
	tree->lcd = NULL;
	tree->rcd = NULL;
	tree->id = ++ID;
	printf("请输入存储到 ID为%d树 的字符>>",tree->id);
	scanf("%c", &data);	getchar();
	tree->data = data;
	push_back(TreeInfo,tree);
left:	//讨论是否创建子树
	printf("是否对ID为%d的树创建左子树?(Y/N)>>",tree->id);
	scanf("%c", &key); getchar();
	if (key == 'Y' || key == 'y') {//如果创建左子树
		TreeCreate(/*&tree,*/tree->lcd);
		tree->lcd->parent = tree;
	}
	else if (key == 'N' || key == 'n') {//如果不创建左子树
		tree->lcd = (pTnode)malloc(sizeof(TNode));
		tree->lcd->data = '#';			//存入占位符
		tree->lcd->lcd = NULL;
		tree->lcd->rcd = NULL;
		tree->lcd->id = -1;
	}
	else goto left;
right:
	printf("是否对ID为%d的树创建右子树?(Y/N)>>",tree->id);
	scanf("%c", &key); getchar();
	if (key == 'Y' || key == 'y') {//如果创建右子树
		TreeCreate(/*&tree,*/tree->rcd);
		tree->rcd->parent = tree;
	}
	else if (key == 'N' || key == 'n') {//如果不创建右子树
		tree->rcd = (pTnode)malloc(sizeof(TNode));
		tree->rcd->data = '#';			//存入占位符
		tree->rcd->lcd = NULL;
		tree->rcd->rcd = NULL;
		tree->rcd->id = -1;
	}
	else goto right;
	return TreeInfo;
}

void TreeOutput(vector v){
	for (int i = 0; i < v.length; i++) {
		printf("节点ID为 %d的树的信息: ", v.data[i]->id);
		printf("数据:%c ", v.data[i]->data);
		if (v.data[i]->parent == NULL) printf("此节点是树根 ");
		else printf("双亲结点ID:%d ", v.data[i]->parent->id);
		if (v.data[i]->lcd->data == '#')printf("没有左子树 ");
		else { printf("左子树ID:%d,对应值为%c ", v.data[i]->lcd->id, v.data[i]->lcd->data); }
		if (v.data[i]->rcd->data == '#')printf("没有右子树 ");
		else { printf("右子树ID:%d,对应值为%c ", v.data[i]->rcd->id, v.data[i]->rcd->data); }
		printf("\n");
	}
}

int gethigh(pTnode tree){
	int hl, hr;
	if (tree->data != '#') {
		hl = gethigh(tree->lcd);
		hr = gethigh(tree->rcd);
		return hl > hr ? hl + 1 : hr + 1;
	}
	else return 0;		//如果不存在,返回0
}
 
void First_Order_View(vector v){
	for (int i = 0; i < v.length; i++) {
		printf("%c ", v.data[i]->data);
		putlink(v.data[i]);
		FlushBatchDraw();
		system("pause");
		putdata(v.data[i]);
		FlushBatchDraw();
		system("pause");
	}
}

void Second_Order_View(pTnode tree){
	if (tree->data != '#') {
		Second_Order_View(tree->lcd);
		printf("%c ", tree->data);
		putdata(tree);
		FlushBatchDraw();
		system("pause");
		Second_Order_View(tree->rcd);
		putlink(tree);
		FlushBatchDraw();
		system("pause");
	}
	else return;
}

void Third_Order_View(pTnode tree){
	if (tree->data != '#') {
		Third_Order_View(tree->lcd);
		Third_Order_View(tree->rcd);
		printf("%c ", tree->data);
		putdata(tree);
		FlushBatchDraw();
		system("pause");
		putlink(tree);
		FlushBatchDraw();
		system("pause");
	}
	else return;
}

void Level_Order_View(pTnode tree){
	vector v = {};
	init_vector(v);
	push_back(v, tree);
	while (v.length) {
		Tnode out = pop_front(v);//取出队头元素
		if (out.lcd->data != '#') push_back(v, out.lcd);//如果有左子树,左子树进队列
		if (out.rcd->data != '#') push_back(v, out.rcd);//如果有右子树,右子树进队列
		printf("%c ", out.data);
		putlink(&out);
		FlushBatchDraw();
		system("pause");
		putdata(&out);
		FlushBatchDraw();
		system("pause");
	}
}

void TreeDestory(vector& v){
	for (int i = 0; i < maxsize; i++) {
		free(v.data[i]);	//v.data里面装的是指向树节点的指针,释放v.data就是释放树
	}
	//free(v.data);			//不需要重复释放了
	v.length = 0;
	printf("\n内存释放完毕...");
}

位置信息配置与图形绘制

void TreeDestory(vector& v){
	for (int i = 0; i < maxsize; i++) {
		free(v.data[i]);	//v.data里面装的是指向树节点的指针,释放v.data就是释放树
	}
	//free(v.data);			//不需要重复释放了
	v.length = 0;
	printf("\n内存释放完毕...");
}

int freshlevel(int treehigh, pTnode tree){
	int high = 0;
	if (tree->parent != NULL) high = tree->parent->level + 1;
	else high = 1;
	tree->level = high;
	printf("%c at level %d ,", tree->data, tree->level);
	return high;
}

int freshposx(pTnode tree){
	pTnode parent = tree->parent;
	int dx = ConsoleX / pow(2, tree->level - 1) / 2;
	if (parent != NULL) {
		if (parent->lcd == tree) tree->pos.x = parent->pos.x - dx;
		else if (parent->rcd == tree)tree->pos.x = parent->pos.x + dx;
		else {
			printf("结构有误!");
			exit(-4);
		}
	}
	else tree->pos.x = dx;
	printf("%c x %d ,", tree->data, tree->pos.x);
	return tree->pos.x;
}

int freshposy(int treehigh,pTnode tree) {
	int dy = ConsoleY / treehigh;
	int ret = dy * (tree->level-1);
	tree->pos.y = ret;
	printf("%c x %d ,", tree->data, tree->pos.y);
	return ret;
}

void makestatus(vector treeinfo){
	int treehight = gethigh(treeinfo.data[0]);
	for (int i = 0; i < treeinfo.length; i++) {
		freshlevel(treehight, treeinfo.data[i]);
	}
	for (int i = 0; i < treeinfo.length; i++) {
		freshposx(treeinfo.data[i]);
		freshposy(treehight, treeinfo.data[i]);
	}
}

void putdata(pTnode tree){
	if (tree->data != '#') {
		setlinecolor(GREEN);
		rectangle(tree->pos.x - offset, tree->pos.y - offset, tree->pos.x + rx + offset, tree->pos.y + ry + offset);
		settextcolor(GREEN);
		settextstyle(40, 0, L"宋体");
		outtextxy(tree->pos.x, tree->pos.y, tree->data);
		printf("put %d %d %c\n", tree->pos.x, tree->pos.y, tree->data);
	}
	else return;
}

void putlink(pTnode tree){
	if (tree->data!='#' && tree->parent != NULL) {
		setlinecolor(BLUE);
		setlinestyle(PS_DASH,3);
		line(tree->parent->pos.x + (rx + offset) / 2, tree->parent->pos.y + ry + offset, tree->pos.x + (rx + offset) / 2, tree->pos.y - offset);
	}
	else return;
}

4.3 main.cpp源文件

#include"MyTree.h"

int main() {
	pTnode tree = NULL;
	vector treeinfo =  TreeCreate(tree);
	tree->parent = NULL;
	TreeOutput(treeinfo);
	int treehight = gethigh(tree);
	printf("树的高度为%d\n", treehight);
	makestatus(treeinfo);
	initgraph(ConsoleX, ConsoleY, EX_SHOWCONSOLE);
	setbkcolor(WHITE);
	cleardevice();
	BeginBatchDraw();

	printf("先序遍历:\n");
	First_Order_View(treeinfo);
	cleardevice();

	printf("\n中序遍历:\n");
	Second_Order_View(tree);
	cleardevice();

	printf("\n后序遍历:\n");
	Third_Order_View(tree);
	cleardevice();

	printf("\n层序遍历:\n");
	Level_Order_View(tree);
	cleardevice();

	outtextxy(0, 0, L"thanks for your using");
	EndBatchDraw();

	getchar();
}

输入输出

输入样例:

A
y
B
y
D
n
n
y
E
y
H
n
n
y
I
n
n
y
C
y
F
n
n
y
G
y
J
n
n
n

输出样例:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 29
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值