C语言可视化二叉排序树

i oy 2022-07-13 18:38:08

一、引言

    二叉排序树的应用是本人《数据结构》的课设大作业,因为想弥补《C语言程序设计》课程设计没能用到图形化编程的遗憾,本次课设尝试用图形化界面让二叉排序树可视化。所有二叉树的代码在附录中给出。

二、目的

  1. 通过课程设计“二叉排序树的应用”,巩固和加深对线性表、栈、查找、排序等理论知识的理解,进一步提升c语言的实践编程能力;
  2. 学习c语言图形化界面的编程,基于easyx.h图形库;
  3. 掌握现实复杂问题的分析建模和解决方法(包括问题描述、系统分析、设计建模、代码实现、结果分析等);
  4. 提高利用计算机分析解决综合性实际问题的基本能力。

三、需求分析

  1. 二叉排序树创建:一次性输入n个关键字从空树开始构建一棵二叉排序树。
  2. 二叉排序树查找:输入关键字,从二叉排序树中查找该关键字是否在二叉排序树中。
  3. 二叉排序树插入:输入关键字,若不在二叉排序树中,则将其插入。
  4. 二叉排序树输出:将二叉排序树中的所有关键字按递增顺序输出。
  5. 二叉排序树关键字保存至文件中与输出文件中的关键字。
  6. 二叉排序树以图形化形式输出;
  7. 整个课设界面右上角有时钟更新;

三  概要设计

1.设计思路

        采用二叉链表作为二叉排序树的存储结构,利用c语言的easyx.h图形库绘制二叉树的树形形状,直观观察二叉排序树的结构。二叉排序树的查找和插入都用二叉树的先序遍历的方法,依次与树的结点关键字进行比较。若要查找或者要插入的关键字比大则往右子树进行遍历比较,反之往左子树进行遍历比较。如果最终树中不存在要插入的关键字,则生成一个结点,遵循关键字左小右大的原则,放在在最后一次比较关键字的结点的左(右)孩子的位置。

2.整个课设功能介绍

 

 

四  详细设计

1二叉排序树创建

1.1 二叉排序树存储结构

        在创建二叉树之前我想定义二叉树的存储结构是很关键的一步,考虑到二叉链表可以大大减少二叉树的空间复杂度,也便于实现二叉排序树插入功能,我使用了二叉链表的存储方式。之后在想到可以借本次课设的机会学了解c语言图形化编程,我在定义二叉排序树的结构体时增加了两个结构体成员,结点的x坐标变量和结点的y坐标变量,存储各个结点的位置,以便后续绘制结点之间的连线。

最终二叉排序树的存储结构定义成以下的形式(ps:typedef int i32;):

/*定义二叉排序树的结构体*/

typedef struct {

    i32 Node_x;//结点x坐标

    i32 Node_y;//结点y坐标

    i32 Node_value;//结点关键字的值

}data;

//二叉链表的定义

typedef struct Tree {

    data key;

    struct Tree* lchild, * rchild;

}BSTNode, * BSTree;

1.2 二叉排序树关键字输入

       由于使用的是图形化界面,输入数据需要通过调用函数Inputbox来实现,但问题是输入的数据是字符串,而本次课设要求是以整型数据作为关键字设计二叉排序树的存储结构,而这是我在做课设遇到的第一个问题,字符数据与整型数据不一致,后来在参考一些图形化界面的博客时发现sscanf函数可以把字符串转化为一个整型数据,后来根据这一点启发,我自己写了一个函数可以将字符串转化为整型数组,但是要按照一个关键字一个空格的输入方式才能保证输入的所有的关键字都能转化成整型数据,到这里就解决了图形化界面数据输入的问题了。

1.2 二叉排序树关键字插入

       二叉排序树关键字插入函数用的递归方法,首先判断要插入的关键字是否存在,若存在则结束插入并提示用户要插入的关键字已存在,若不存在则判断此关键字与根结点关键字的大小关系,若较大,则往右递归,反之往左递归,直到最终判读到某结点不存在左(右)孩子新生成一个结点存放插入的关键字,并结束递归。

1.4 二叉排序树的创建

       二叉树排序树的创建就是不断的调用二叉排序树的插入函数,利用循环调用二叉排序树的插入函数将整型数组中所有关键字存入二叉排序树中,循环结束后整个创建二叉排序树的过程就结束了。

2二叉排序树的输出

2.1 整棵树的绘制

        由于是图形化的形式输出二叉排序树,所以我首先要给每个结点都赋予x和y坐标,从而能够有条理的绘制出整棵树的形状,并且能够方便查找到某个关键字时改变结点颜色显示出查找到的关键字所在结点在树中的相对位置,更直观看到查找到的关键字的位置。

        为了实现这个目的,我首先尝试在创建二叉排序树的时候将每个结点都赋坐标值,但是由于要递归依次插入结点的原因导致,我并不能将每一个结点的x,y变量都成功赋值,所以在创建过程中赋值的方法以失败告终。其后我突然想到,能不能重新写一个函数来给结点x,y变量赋值呢?顺着这个思路往下挖掘,重新写一个函数,此时是在已经创建二叉排序树之后的事了,肯定是要遍历整棵二叉树才能给每一个结点的坐标变量赋值。没有过多的理由,我选择了先根遍历的方法遍历树,虽然也是用的递归,但是这个递归成功的让我给每一个结点的坐标值都赋值了。

        记录下了每一个结点的坐标值之后,我需要在屏幕上绘制每一个结点和结点之间的连线。为了美观,我需要规划每一层兄弟结点之间的远近关系。为此我设计了结点与根结点越远,兄弟结点之间距离越远的几何关系。结点能绘制出来了,但是父与子之间的关系并不能表现出来,然后我就在父结点与子结点之间增加了一条线,至此一颗完整树的输出就完成了。如下图:

 

2.2 关键字从小到大的输出

       二叉排序树在创建的时候,遵循了结点关键字“左小右大”的原则,所以我要实现关键字从小到大的这个目标,就可以转化成先根遍历整棵树,然后输出先根每次访问到的结点关键字即可。依照这个思路,很快就实现了这个操作,输出图形如下:

 

 

3、二叉排序树的查找

         二叉排序树关键字的查找过程其实与插入的思路大致相同,都是利用结点关键字“左小右大”的原则。不同的是,查找在找到关键字后,会将我查找到的关键字结点变为蓝色,如下图(我查找关键字‘60’)

 

 

4、二叉排序树关键字的保存

4.1 存入文件

         关键字的保存,这个是困扰我很久的一个问题,起初没想到很好的方法提取出树中的关键字,用递归总是会出一些错误。后来暂时用了输入时创建的字符串暂存文件中实现树的关键字的保存功能,这导致保存这里出现了一个bug,即我后面如果插入一个结点然后再保存整棵树并不能把插入后的那个结点也保存进来的。

         但是在做完所有功能之后,我觉得是时候修复这个bug的时候了。想着取出树中的关键字要跟给结点的坐标变量赋值一样都要遍历整棵树,但是递归的方法在这里失效了。还是基于遍历整个树,我上网查了很多关于遍历树的方法,后来借鉴了用栈遍历树的方法,解决了这个问题。其具体代码如下:

/*************************************

*

*   @ 函数名:Pre_BST

*   @ 形参  :BSTree T

*   @ 功能  :非递归先序遍历,取出二叉链表中的关键字

*   @ 返回值:取出的关键字数组

*

***************************************/

static i32 Pre_Buffer[200];//暂存二叉链表中的关键字

i32* Pre_BST(BSTree T) {

    BSTree St[200], p = NULL; //定义一个栈,和一个存放根节点的指针变量

    i32* Pre_Buffer = (i32*)malloc(sizeof(i32*) * 200);

    if (Pre_Buffer == NULL)return NULL;

    i32  top = -1,Pre_Count = 0; //初始化栈

         if (T != NULL) {

             St[++top] = T;  //根节点进栈

             while (top > -1) {

                  p = St[top--];

                  Pre_Buffer[Pre_Count++] = p->key.Node_value;//取出关键字,暂存在数组Pre_Buffer中

                  if (p->rchild != NULL) { //右孩子进栈

                      St[++top] = p->rchild;

                  }

                  if (p->lchild != NULL) { //左孩子进栈

                      St[++top] = p->lchild;

                  }

             }

         }

    return Pre_Buffer;

}

 

        至此,虽然取出了链表中的关键字,但是在存入文件中要以字符串的形式传入。这里我想到了sprintf函数,可以将整型数据格式化成字符串,再借以循环可以把整型数组中所有的整型元素转化为字符串然后存入文件中了,但是也是以一定的格式传入文件的,即一个关键字一个空格,最后以0作为结束标志。

如下图,存入文件中的数据:

 

 

4.2 从文件取出

      从文件中取出数据比较好操作,之前存入文件的所有数据相当于是一个字符串,我只需要从文件中取出这个字符串存入一个字符型数组即可。而且有之前存入的铺垫,此时的字符型数组中的数据也是有一定格式的,我再调用将字符型数组转化为整型数组的函数把数据类型转化一下就可以用于创建树的关键字数据了。

5 界面的时钟栏

       源于c语言课程设计的标准界面思想,我尝试做一个图形化的简化版标准界面,只做时钟栏。想到时钟,时钟库浮现在我的脑海中,我只需要调用时钟库里面的函数即可得到此时此刻的时间。那么接下来就是输出这个时间的问题了,时钟的秒是每秒都要变化的,而分是每分钟变化的。这意味着,我每一秒都要把上一秒的清除掉然后输出这一秒,那么分、时、日、月、年也是这样的逻辑。经实践发现,可行,由于时间是动态变化的图片不太好显示其特点,这里不予展示。

五  调试分析

1、二叉排序树无法二次绘制&第二次输出从小到大的结点时开始位置是上一次结束的后方位置  

         在调试代码的时候发现不能创建两次树,即无法在屏幕上绘制第二次。经过VS自带的调试功能,用逐语句调试后,发现能正常创建二叉树到二叉链表中,但是不能在屏幕上绘制出来。

        后来再次逐语句调试绘制二叉树的函数,发现是每个结点的坐标变量的值是一个绝对值很大的负数,接着调试发现是我设置的一个给结点坐标赋值的一个标志导致第二次无法给树结点坐标赋值。

        但是我这里又是用的递归赋值,又不能在这个函数里面给标志初始化,后来我又想到了可以在主函数里面用外部变量给这个标志再初始化,实操后这两个问题都解决了。

2、定义坐标变量的想法

       基于看到的一些优秀的用其他编程语言编写的可视化二叉树,我也想实现将查找到的关键字所在结点变色,显示查找成功。可能是与同学的思想碰撞让我想到给每个结点都配上一个坐标变量以存储其坐标,方便我找到在屏幕上的关键字所在结点,然后再改变其颜色。而且这个坐标变量让我在调整树结点之间的几何关系上也便利了很多。

3、绘制树的形状比较奇怪

       在第一次绘制一棵树在屏幕上的时候,由于没有控制好输出各个结点的位置关系,导致整棵树都看起来很奇怪,也是慢慢调试更换数据调出一个比较合适的树的形状。

六  测试结果

1、二叉排序树的创建

一次性输入“100 80 60 89 90 120 110 130 0”关键字创建一个二叉树,结果图如下:

2、二叉排序树的查找

   查找关键字“60”,输入“60 0”,结果图如下:

3、二叉排序树的关键字输出

关键字从小到大依次输出,结果图如下:

4、二叉排序树的保存

   保存关键字至文件中,结果图如下:

 

5、二叉排序树的插入

   插入关键字“59”,输入“59 0”,结果图如下:

6、绘制最近保存树

     结果图如下:

 

7、用户指南(帮助)

      结果图如下:

8、退出

如果用户按确认则退出程序,否则继续运行

 

七  用户使用说明

1、本程序简介

        欢迎您使用程序“二叉排序树的应用”,该程序由作者zyn开发,徐科老师指导完成。含C量95%+,夹杂少许C++。采用图形化界面的方式,不再以单纯的黑框控制台依靠输入编号序号实现功能,取而代之以各种图形按键以及鼠标按键操作,希望能带给您不一样的体验感和观感。

2、说明

       进入本程序后,您可以看到界面左侧有八个功能按键,从上到下依次是:帮助、创建二叉树、查找、中序遍历、插入、保存、绘制最近保存树和退出,界面右侧有时钟显示,而标题栏下方和功能按键右侧都是二叉树的绘制区域。鼠标在按键内,按键会变色,如果情况与此相反退出重新进入程序即可恢复;如果打开程序发现时钟栏只有秒钟在动,重启几次程序时钟日期即可重新显示出来。

3、注意事项

       1.创建树时输入关键字的要求每输入一个关键字就要输入一个空格最后以“0”作为结束标志,例如输入关键字“100 80 60 89 90 120 110 130 0”;

       2.查找关键字时输入关键字要以“xx 0”格式查找,例如找“60”,则输入“60 0”;

       3.插入关键字时输入方式与查找相同;

       4.保存功能是可以保存用插入功能插入的关键字的;

4、按键介绍

       4.1在本段说明中列举的一些程序注意事项和程序操作方法您在进入程序后可以点击“帮助”按键查看;

       4.2 “创建二叉树”按键按下后,会弹出一个弹框,框内可以输入n个关键字创建一颗二叉排序树,按照特定格式输完后按下确认即可创建

       4.3 “查找”按键按下后,跟“创建二叉树”按下一样会弹框,操作也与其一样,值得一提的是,每次查找只能查找一个关键字;

       4.4 “插入”按键,操作介绍与同上;

       4.5 “中序遍历”按键按下后,在功能按键的下方会出现蓝色的带关键字的圈,其一串关键字,从左到右是按递增排列的;

       4.6 “保存”按键,可以将现屏幕中显示的二叉树中所有关键字全部存入文件中;

       4.7 绘制最近保存树”按键可以绘制上一次保存在文件中的树;

       4.8 “退出”按键,按下后会弹框,其中有两个选择,一是“是”另一个是“否”,若再按下“是”,则退出程序,若按下“否”,程序继续运行。

八  课程设计总结

        前期看着要求构思规划整个课设的各个模块、中期对各个模块进行调试分析bug的和最后看到成品的喜悦可以恰到好处得描述我做整个课设的过程。

前期

       拿到课设题目要求时,仔细阅读题目把握每一个要求实现的先后顺序的优先度,按照这个优先度去实现相应的要求功能,让做课设的效率提高了很多,相比我做c语言课设。有了第一次c语言课程设计的经验,我懂得了在最开始给整个工程分模块并且合理的统筹安排它们可以让我在后续调试过程中省下很多时间,而这一次的课设最总效果也确实是这样,算是我在c语言课设中获得经验的一次实践。

中期

       如果用一句话简述我中期做的事,那我一定会用“发现问题,解决问题”来描述。在写完代码后检验所写的是否能达到自己的目的,如果没有达到,第一个方法“粗看代码”,即检查代码是否都书写到位,换言之该写的是否都写上去了,比如我想打印输出“hello world”,我就去检查代码中是否存在语句“printf(“hello world!”);”第二个方法就是用编译器自带的Debug调试功能了,观察每一次循环或者选择功能后某一个变量的值是否是我所预期的值。

 后期

   经过了前中期的打磨,后期是最终检查程序的时候,对比现已实现的功能和课设要求实现的功能,查漏补缺的一个阶段,在本次的课设中要求实现的功能我都已经实现,并且是增加了四个功能,分别是  1. 二叉排序树关键字保存至文件中与输出文件中的关键字;

                                                                                        2. 二叉排序树以图形化形式输出;

                                                                                        3. 整个课设界面右上角有时钟更新;

                                                                                        4. 输出用户指南。

写在最后

       本次课设给我带来最大的收获就是进一步锻炼了我发现问题、解决问题以及统筹解决问题的能力。在完成课设后看着自己的作品,感到无限的自豪和用代码去创造一些东西的喜悦,也让我体会到开发一个我们当前便捷生活来之不易,如当今各种小程序和APP能给我们带来极大的便利,但我现在也了解到了其开发的难度。“莫等闲,白了少年头,空悲切”。

九、附录

1.代码中的简化定义

typedef char               i8;
typedef unsigned char      u8;
typedef short              i16;
typedef unsigned short     u16;
typedef int                i32;
typedef unsigned int       u32;
typedef long long          i64;
typedef unsigned long long u64;
typedef float              f32;
typedef double             f64;

2.所有关于树的代码

/*************************************
*
*	@ 函数名:Insert_BST
*	@ 形参  :BSTree* T, ElemType e
* 	@ 功能  :二叉排序树的插入
*	@ 返回值:无
*
***************************************/

bool Insert_BST(BSTNode*& p, int element)
{
	if (NULL == p) // 空树  
	{
		p = new BSTNode;

		p->key.Node_value = element;
		p->lchild = p->rchild = NULL;
		return true;
	}

	if (element == p->key.Node_value) // BST中不能有相等的值 
		return false;

	if (element < p->key.Node_value)  // 递归  
		return Insert_BST(p->lchild, element);

	else
		return Insert_BST(p->rchild, element); // 递归  
}

void Draw_Insert_BST(BSTree T) {
	i8 Insert_Key_Buffer[20] = "";  //输出插入关键字缓存区
	i32 Insert_key = 60;//默认插入的关键字是60
	InputBox(Insert_Key_Buffer, 200, "请输入关键字", "插入关键字", "60 0", 0, 0, true);

	if (CArry_to_IArry(Insert_Key_Buffer)) {
		Insert_key = *CArry_to_IArry(Insert_Key_Buffer);
		if (!Insert_BST(T, Insert_key)) {
			MessageBox(NULL, "插入失败,插入的关键字在树中已存在", "插入", MB_OK | MB_SYSTEMMODAL);
			return;
		}
		clearrectangle(96, 30, 1400, 800);
		clearrectangle(0, 600, 1400, 800);

		setlinestyle(PS_SOLID, 1);
		Draw_BST(T);//重新绘制二叉树
	}

}

/*************************************
*	@ 函数名:Create_BST
*	@ 形参  :BSTree* T
* 	@ 功能  :二叉排序树的创建
*	@ 返回值:无
***************************************/

void Create_BST(BSTNode*& T, int a[]) {
	T = NULL;
	int i;
	if (a) {
		for (i = 0; a[i] != 0; i++) {
			Insert_BST(T, a[i]);
		}
	}
}




/*************************************
*
*	@ 函数名:Depth_BST
*	@ 形参  :BSTree T
* 	@ 功能  :求二叉排序树的深度
*	@ 返回值:返回树的深度
*
***************************************/
i32 Depth_BST(BSTree T) {
	i32 m = 0, n = 0;
	if (!T)return 0;
	else {
		m = Depth_BST(T->lchild);
		n = Depth_BST(T->rchild);
		if (m > n) return m + 1;     // 返回左右子树中深度最大的一个
		else return n + 1;
	}
}

/*************************************
*
*	@ 函数名:Search_BST
*	@ 形参  :BSTree T,i32 key
* 	@ 功能  :二叉排序树的递归查找
*	@ 返回值:如查找成功则返回指向该数据元素结点的指针,否则返回空指针
*
***************************************/

BSTree Search_BST(BSTree T, i32 key) {
	if ((!T) || key == T->key.Node_value)return T;
	else if (key < T->key.Node_value) return Search_BST(T->lchild, key);
	else if (key > T->key.Node_value) return Search_BST(T->rchild, key);
	else return NULL;
}
/*************************************
*
*	@ 函数名:Draw_Search_BST
*	@ 形参  :i32 Node_x,i32 Node_y,i32 value   结点的横纵坐标和值
* 	@ 功能  :绘制一个结点
*	@ 返回值:无
*
***************************************/

void Draw_Search_BST(BSTree T) {
	i32 Search_key = 60;
	i8 Search_Key_Buffer[20] = "";   //输入查找关键字缓存区
	memset(Search_Key_Buffer, '\0', sizeof(Search_Key_Buffer));
	InputBox(Search_Key_Buffer, 200, "请输入关键字", "查找关键字", "60 0", 0, 0, true);  //输入要查找的关键字
	if (CArry_to_IArry(Search_Key_Buffer)) {
		Search_key = *CArry_to_IArry(Search_Key_Buffer);                                            
		BSTree P = Search_BST(T, Search_key);
		if (P) {
			Draw_Node(P->key.Node_x, P->key.Node_y, P->key.Node_value, 1);
			MessageBox(NULL, "查找成功", "查找", MB_OK | MB_SYSTEMMODAL);
		}
		else
			MessageBox(NULL, "查找失败", "查找", MB_OK | MB_SYSTEMMODAL);
	}

}
/*************************************
*
*	@ 函数名:Draw_Node
*	@ 形参  :i32 Node_x,i32 Node_y,i32 value ,i32 color 结点的横纵坐标和值 选择颜色 1: 红色 2: 蓝色 defualt: 绿色
* 	@ 功能  :绘制一个结点
*	@ 返回值:无
*
***************************************/
void Draw_Node(i32 Node_x, i32 Node_y, i32 value, i32 color) {
	i32 radius = 20;                   //定义圆的半径  为  15
	i8 value_s[10] = "";               //定义一个接收整型数据的数组,初始化为空
	sprintf_s(value_s, 5, "%d", value);//将整型数据转化为字符串
	switch (color) {
	case 1:
		setfillcolor(RED);
		break;
	case 2:
		setfillcolor(RGB(167,236,255));
		break;
	default:
		setfillcolor(GREEN);
	}

	fillcircle(Node_x, Node_y, radius);         //绘制一个圆
	RECT R1 = { Node_x - radius,Node_y - radius,Node_x + radius,Node_y + radius };
	settextcolor(BLACK);
	drawtext(value_s, &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //在圆的外切矩形的正中央输出文字
}

/*************************************
*
*	@ 函数名:Draw_BST
*	@ 形参  :BSTree T
* 	@ 功能  :绘制一棵树,并给树的结点坐标赋坐标值
*	@ 返回值:无
*
***************************************/
const i32 R_Node_xx = 800; //根结点的横坐标
const i32 R_Node_yy = 100;//根结点的纵坐标
static i32 yy = 40;     //y 方向的增量
i32 flag_x_y = 0;   //标志是否进入子树的坐标赋值
void Draw_BST(BSTree T) {
	if (!T) return; 

	if (!flag_x_y) {
		T->key.Node_x = R_Node_xx;
		T->key.Node_y = R_Node_yy;
	}
	Draw_Node(T->key.Node_x, T->key.Node_y, T->key.Node_value, 0);//绘制结点


	if (T->lchild) {  //如果左孩子存在,给做孩子的x坐标赋值相对位置  T->key.Node_x - Depth_BST(T->lchild) * 30   y坐标赋值  T->key.Node_y + yy
		T->lchild->key.Node_x = T->key.Node_x - Depth_BST(T->lchild) * 30;
		T->lchild->key.Node_y = T->key.Node_y + yy;
		setlinecolor(GREEN);
		line(T->key.Node_x, T->key.Node_y, T->lchild->key.Node_x, T->lchild->key.Node_y);
		flag_x_y = 1;
	}
	if (T->rchild) {//如果左孩子存在,给做孩子的x坐标赋值相对位置  T->key.Node_x + Depth_BST(T->lchild) * 30   y坐标赋值  T->key.Node_y + yy
		T->rchild->key.Node_x = T->key.Node_x + Depth_BST(T->rchild) * 30;
		T->rchild->key.Node_y = T->key.Node_y + yy;
		setlinecolor(GREEN);
		line(T->key.Node_x, T->key.Node_y, T->rchild->key.Node_x, T->rchild->key.Node_y);
		flag_x_y = 1;
	}
	/*递归画出二叉排序树*/
	Draw_BST(T->lchild);
	Draw_BST(T->rchild);
}
/*************************************
*
*	@ 函数名:Draw_BST_Finish
*	@ 形参  :BSTree* T
* 	@ 功能  :输入一组关键字,最终在画布上绘制一棵树
*	@ 返回值:无
*
***************************************/
static i8 Input_Buffer[200]; //输入树的所有关键字存放区 

void Draw_BST_Finish(BSTree* T) {
	clearrectangle(96, 30, 1400, 800);
	clearrectangle(0, 600, 1400, 800);
	memset(Input_Buffer, '\0', sizeof(Input_Buffer));
	InputBox(Input_Buffer, 200, "请输入树", "创建树", "100 80 90 60 120 110 130 0", 0, 0, true);//默认输入关键字 "100 80 90 60 120 110 130 0"
	Create_BST(*T, CArry_to_IArry(Input_Buffer));//创建二叉树
	setlinestyle(PS_SOLID, 1);
	Draw_BST(*T);          //绘制二叉树
}


/********************************
*	@ 函数名:Save_Bst
*	@ 形参  :i8 Buffer[]
* 	@ 功能  :将树按照Inputbox中的输入的格式存入文件中
*	@ 要求  :传入一个字符型的数组
*	@ 返回值:无
*********************************/

void Save_Bst(BSTree T) {
	FILE* tfp = fopen("Binary_Sort_Tree.txt", "w");//新建一个空文档
	i32* Save_Buffer_I = Pre_BST(T), i = 0,flag = 0;
	i8* Save_BUffer_C = (i8*)malloc(sizeof(i8*) * 10);
	if (!Save_BUffer_C)return;
	while (Save_Buffer_I[i]!=0) {
		if (Save_Buffer_I[i] > 10000) continue;
		sprintf(Save_BUffer_C,"%d", Save_Buffer_I[i]);
		fputs(Save_BUffer_C, tfp);
		fputc(' ', tfp);
		i++;
		flag = 1;
	}
	fputc('0', tfp);//只能暂存刚开始输出进来创建的树,后来插入的不能存放进来
	if (flag) {				  //暂时为解决怎么从二叉链表中取出关键字
		MessageBox(NULL, "保存成功", "保存", MB_OK | MB_SYSTEMMODAL);
	}
	else
		MessageBox(NULL, "保存失败", "保存", MB_OK | MB_SYSTEMMODAL);
	fclose(tfp);
	free(Save_BUffer_C);
}

void Draw_Save_BST(BSTree *T) {
	i8* File_Buffer = (i8*)malloc(sizeof(i8*) * 200 + 1); if (!File_Buffer) return;   //存储从文件中取出的数据

	clearrectangle(96, 30, 1400, 800);
	clearrectangle(0, 600, 1400, 800);
	setlinestyle(PS_SOLID, 1);
	File_Buffer = Fetch_BST();
	if (!File_Buffer) MessageBox(NULL, "绘制失败", "绘制文件中的树", MB_OK | MB_SYSTEMMODAL);
	else {
		Create_BST(*T, CArry_to_IArry(File_Buffer));
		Draw_BST(*T);          //绘制二叉树
	}
	free(File_Buffer);
}

/*************************************
*
*	@ 函数名:Ino_Out_BST
*	@ 形参  :BSTree T
* 	@ 功能  :中根遍历树,使得从小到大输出关键字
*	@ 返回值:无
*
***************************************/
i32 pre_x = 20;
i32 pre_y = 650;
void Ino_Out_BST(BSTree T) {
	if (T) {
		Ino_Out_BST(T->lchild);

		Draw_Node(pre_x, pre_y, T->key.Node_value, 2);
		if (pre_x < 780) pre_x += 40;
		else {
			pre_x = 20;
			pre_y += 40;
		}

		Ino_Out_BST(T->rchild);
	}
}

/*************************************
*
*	@ 函数名:Pre_BST
*	@ 形参  :BSTree T
* 	@ 功能  :非递归先序遍历,取出二叉链表中的关键字
*	@ 返回值:取出的关键字数组
*
***************************************/
static i32 Pre_Buffer[200];//暂存二叉链表中的关键字
i32* Pre_BST(BSTree T) {
	BSTree St[200], p = NULL; //定义一个栈,和一个存放根节点的指针变量
	i32* Pre_Buffer = (i32*)malloc(sizeof(i32*) * 200);
	if (Pre_Buffer == NULL)return NULL;
	i32  top = -1,Pre_Count = 0; //初始化栈
		if (T != NULL) {
			St[++top] = T;  //根节点进栈
			while (top > -1) {
				p = St[top--];
				Pre_Buffer[Pre_Count++] = p->key.Node_value;//取出关键字,暂存在数组Pre_Buffer中
				if (p->rchild != NULL) { //右孩子进栈
					St[++top] = p->rchild;
				}
				if (p->lchild != NULL) { //左孩子进栈
					St[++top] = p->lchild;
				}
			}
		}
	return Pre_Buffer;
}

/*************************************
*
*	@ 函数名:Draw_Ino_BST
*	@ 形参  :BSTree T
* 	@ 功能  :绘制中根遍历的关键字
*	@ 返回值:无
*
***************************************/
void Draw_Ino_BST(BSTree T) {
	clearrectangle(0, 600, 1400, 800);
	Ino_Out_BST(T);
}

/********************************
*	@ 函数名:Fetch_BST
*	@ 形参  :void
* 	@ 功能  :取出文件中保存的内容
*	@ 要求  :无
*	@ 返回值:字符型的数组
*********************************/

i8* Fetch_BST(void) {
	FILE* tfp = fopen("Binary_Sort_Tree.txt", "r");//新建一个空文档
	i8* Buffer = (i8*)malloc(sizeof(i8) * 500 + 1);
	if (!Buffer) return NULL;
	fgets(Buffer,200, tfp);//直接从文件中获取字符串
	fclose(tfp);
	return Buffer;//返回从文件中取得的字符
}

 

...全文
547 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

1,040

社区成员

发帖
与我相关
我的任务
社区描述
中南民族大学CSDN高校俱乐部聚焦校内IT技术爱好者,通过构建系统化的内容和运营体系,旨在将中南民族大学CSDN社区变成校内最大的技术交流沟通平台。
经验分享 高校 湖北省·武汉市
社区管理员
  • c_university_1575
  • WhiteGlint666
  • wzh_scuec
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

欢迎各位加入中南民族大学&&CSDN高校俱乐部社区(官方QQ群:908527260),成为CSDN高校俱乐部的成员具体步骤(必填),填写如下表单,表单链接如下:
人才储备数据库及线上礼品发放表单邀请人吴钟昊:https://ddz.red/CSDN
CSDN高校俱乐部是给大家提供技术分享交流的平台,会不定期的给大家分享CSDN方面的相关比赛以及活动或实习报名链接,希望大家一起努力加油!共同建设中南民族大学良好的技术知识分享社区。

注意:

1.社区成员不得在社区发布违反社会主义核心价值观的言论。

2.社区成员不得在社区内谈及政治敏感话题。

3.该社区为知识分享的平台,可以相互探讨、交流学习经验,尽量不在社区谈论其他无关话题。

试试用AI创作助手写篇文章吧