数据结构课程设计——二叉树的构造

本文详细介绍了如何使用C++编程语言,通过先序遍历和中序遍历或者层次遍历构建二叉树,包括递归方法的实现、存储结构以及示例测试。
摘要由CSDN通过智能技术生成

前言

NEFU,计算机与控制工程学院,基于C/C++的数据结构 ,五个课程设计

环境

操作系统:Windows 10
IDE:Visual Studio Code、Dev C++ 5.11、Code::Blocks

课设目录

其他联系方式:

Gitee:@不太聪明的椰羊

B站:@不太聪明的椰羊

ps:一定要注明来意!

一、需求和规格说明

1.1 问题描述

        已知二叉树的层序和中序遍历序列,或已知二叉树的先序序列、中序序列,试编写算法建立该二叉树( 用递归或非递归的方法都可以)。

 1.2 要求

        能够输入树的各个结点,并能够输出用不同方法遍历的遍历序列;分别建立建立二叉树存储结构的的输入函数、输出层序遍历序列的函数、输出先序遍历序列的函数。

二、设计

2.1 设计思想

先序+中序建立二叉树:
        通过先序获得根节点,找到根节点在中序序列中对应的位置,该根节点将中序序列分为左子树中序序列和右子树中序序列,递归调用函数,利用左子树的先序+中序再次划分,利用右子树的先序+中序再次划分,直到子树序列为空。

层序+中序建立二叉树:
        (法1未通过代码实现):通过层序获得根节点,找到根节点在中序序列中对应的位置,该根节点将中序序列分为左子树中序序列和右子树中序序列,找到左子树的层序序列和右子树的层序序列,递归调用函数,利用左子树的层序+中序再次划分,利用右子树的层序+中序再次划分,直到子树序列为空。
        (法2代码实现):依次遍历层序,将层序元素插入二叉树,从根结点开始,利用中序序列中的位置比较层序元素是根结点的左子树元素还是右子树元素,递归查找,直到查找到NULL,将层序元素插入到此NULL上,并将该点的左右子树设为NULL。

2.2 设计表示(存储结构)

typedef struct Binode
{
    char data;
    struct Binode *lchild, *rchild;
}BiNode,*Bitree;

三、解决方案

3.1功能示意图

3.2流程示意图

(一)先序+中序递归建立二叉树

(二)层序+中序递归建立二叉树

(三)递归先序输出二叉树

(四)递归中序输出二叉树

(五)递归后序输出二叉树

(六)层次遍历输出二叉树

四、调试报告

(1)

输入先序遍历序列:ABDC,中序遍历序列:BDAC

输入层次遍历序列:ABCD,中序遍历序列:BDAC

(2)

输入先序遍历序列:ABCDEFG,中序遍历序列:CDBAEFG

输入层次遍历序列:ABCDEF,中序遍历序列:DBEACF

五、代码

#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstring>
using namespace std;
#define max 1000
typedef struct Binode
{
    char data;
    struct Binode *lchild, *rchild;
}BiNode,*Bitree;

void levmid(Bitree &T, char kid, int kidmid, string mid, int l);

void cinpremid(string &pre,string &mid)
{
    cout << "请输入先序遍历序列:";
    cin >> pre;
    cout << "请输入中序遍历序列:";
    cin >> mid;
}

void cinlevmid(string &lev,string &mid)
{
    cout << "请输入层次遍历序列:";
    cin >> lev;
    cout << "请输入中序遍历序列:";
    cin >> mid;
}

Bitree premidCreatTree(string pre, int prebegin, int preend, string mid, int midbegin, int midend) 
{//先序+中序建立二叉树
    Bitree T;
    if(preend-prebegin+1==0)//子树先序序列为空,先序序列与中序序列长度相同
    {
        T = NULL;
        return T;
    }
    int midroot = 0, i;//midroot为中序序列中根的位置
    for(i = midbegin; i <= midend; i++)
    {
        if(mid[i] == pre[prebegin])//先序序列的第一个点为根结点
        {
            midroot = i;//中序序列中根结点的位置
            break;
        }
    }
    T = new BiNode;
    T->data = pre[prebegin];//先序序列的第一个结点为根
    T->lchild = premidCreatTree(pre, prebegin + 1, prebegin + (midroot - midbegin), mid, midbegin, midroot - 1);
    //(prebegin + 1)~(prebegin + (midroot - midbegin))为左子树先序序列
    //(midbegin)~( midroot - 1)为左子树中序序列
    T->rchild = premidCreatTree(pre, preend - (midend - midroot) + 1, preend, mid, midroot + 1, midend);
    return T;
}

/*此段代码的函数没有被使用
Bitree levmidCreatTree(string mid, string lev, int midbegin, int midend) 
{
    Bitree T;
    string llv, rlv;
    if(midend-midbegin+1<=0)
    {
        T = NULL;
        return T;
    }
    int midroot = 0, i; // midroot为中序序列中根的位置
    for(i = midbegin; i <= midend; i++)
    {
        if(mid[i] == lev[0])//层次序列的第一个点为根结点
        {
            midroot = i;//中序序列中根结点的位置
            break;
        }
    }
    T = new BiNode;
    T->data = lev[0];//层次序列的第一个结点为根
    llv = levstr(mid, midbegin, midroot - 1, lev);//左子树的层次序列
    rlv = levstr(mid, midroot + 1, midend, lev);//右子树的层次序列
    T->lchild = levmidCreatTree(mid, llv, midbegin, midroot - 1); // 左子树
    T->rchild = levmidCreatTree(mid, rlv, midroot + 1, midend);// 右子树
    return T;
}

string levstr(string mid, int midbegin, int midend, string lev)//寻找子树的层次遍历序列
{
    string result;
    int lv = 0, i;
    for (i = 0; i < (int)lev.length(); i++)
    {
        if (have(mid, midbegin, midend, lev[i]))
        {
            result[lv] = lev[i];
            lv++;
        }
    }
    return result;//子树的层次遍历序列
}

int have(string str, int begin, int end, char ch)//判断字符串str从begin到end之间有无ch字符
{
    int i;
    for (i = begin; i <= end; i++)
    {
        if (str[i] == ch)
        {
            return 1;
        }
    }
    return 0;
}
*///levstr()返回有错误,有兴趣的同学可以尝试修改一下

Bitree levmidCreatTree(Bitree &T,int l,string mid,string lev)
{//层次+中序建立二叉树
   int kidmid, i;
   T = NULL;
   for(i = 0; i < l; i++)//每一个层序元素依次插入二叉树
   {
        for(kidmid = 0; kidmid < l; kidmid++)
        if(mid[kidmid]==lev[i])//kidmid为层序元素在中序中的位置
          break;
        levmid(T, lev[i], kidmid, mid, l); // lev[i]为层序元素,T为根结点
   }
    return T;
}

void levmid(Bitree &T,char kid,int kidmid,string mid,int l)//kid为要插入点,kidmid为要插入点在中序中的位置
{
   if(T==NULL)
   {//从头结点开始,比较kid是被查找结点的左孩子还是右孩子,直到查找到NULL,每一个被查找点都是kid的祖先
        T = new BiNode;
        T->lchild = NULL;
        T->rchild = NULL;
        T->data = kid;//查找到NULL,将kid插入在此位置上
        return;//插入结束
   }
   int rootmid;//kid的祖先在中序中的位置
   for (rootmid= 0; kidmid < l; rootmid++)
   {
       if(mid[rootmid]==T->data)
        break;
   }
   if(kidmid<rootmid)//kid在中序中的位置在祖先左侧,为左孩子
       levmid(T->lchild, kid, kidmid, mid, l);
   else
       levmid(T->rchild, kid, kidmid, mid, l);
}

void dlroutput(Bitree T)//先序输出树
{
    if(T)
    {
        cout << T->data;//先序
        dlroutput(T->lchild);//左孩子
        dlroutput(T->rchild);//右孩子
    }
}

void ldroutput(Bitree T)//中序输出树
{
    if(T)
    {
        ldroutput(T->lchild);//左孩子
        cout << T->data;//中序
        ldroutput(T->rchild);//右孩子
    }
}

void lrdoutput(Bitree T)//后序输出树
{
    if(T)
    {
        lrdoutput(T->lchild);//左孩子
        lrdoutput(T->rchild);//右孩子
        cout << T->data;//后序
    }
}

void levoutput(Bitree T)//利用队列层次遍历输出树
{
    if(T==NULL)
        return;
    queue<Bitree> q;
    Bitree p;//end存当层最右边位置,nextend存下一层最右边位置
    q.push(T);//根入队
    while(!q.empty())
    {
        p = q.front();//p==队首
        q.pop();//队首出队
        cout << p->data;
        if(p->lchild!=NULL)//左孩子入队
        {
            q.push(p->lchild);
        }
        if(p->rchild!=NULL)//右孩子入队
        {
            q.push(p->rchild);
        }
    }
}

void output(Bitree T)
{
    cout << endl;
    cout << "二叉树的先序遍历序列:";
    dlroutput(T);
    cout << endl;
    cout << "二叉树的中序遍历序列:";
    ldroutput(T);
    cout << endl;
    cout << "二叉树的后序遍历序列:";
    lrdoutput(T);
    cout << endl;
    cout << "二叉树的层次遍历序列:";
    levoutput(T);
    cout << endl;
    cout << endl;
    cout << endl;
}

int main()
{
    Bitree T1,T2;
    int preend, midend, l;
    string pre, mid, lev;

    cinpremid(pre, mid);
    preend = pre.length() - 1;
    midend = mid.length() - 1;
    T1 = premidCreatTree(pre,0,preend,mid,0,midend);//先序+中序建立二叉树
    output(T1);

    cinlevmid(lev, mid);
    l = lev.length();
    T2 = levmidCreatTree(T2, l, mid, lev);//层次+中序建立二叉树
    output(T2);
    return 0;
}

/*
test1:
输入先序遍历序列:ABDC,中序遍历序列:BDAC
输入层次遍历序列:ABCD,中序遍历序列:BDAC

test2:
输入先序遍历序列:ABCDEFG,中序遍历序列:CDBAEFG
输入层次遍历序列:ABCDEF,中序遍历序列:DBEACF
*/

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程设计题目 一、必做题。 1、链表排序 任务 : (1)从文件读入30个无序整数,建立一个单链表,排序输出、再倒序输出。 (2)从文件A读入30个无序整数,建立一个递增的单链表A并输出,从文件B读入30个无序整数,建立一个递增的单链表B并输出,在A中求递增的并集。 (3)从文件读入30个学生成绩(0-100之间),建立一个双向循环链表并输出,调整链表顺序,使所有的及格成绩排在不及格成绩之前,并输出。 2、二叉树的应用 任务 :编程实现二叉树的建立,层次遍历,(递归和非递归方法)先序、中序、后序,二叉树的高度、宽度。二叉排序树的建立、插入、删除; 基本要求:从文件中读入建树信息,树的节点数目不小于20个,树的高度不小于5; 3、校园局域网布线和游历问题 任务 :用无向网表示你所在学校的主要建筑平面图,图中顶点表示主要建筑,图中的边表示建筑之间的道路,存放路径长度信息。要求能够建立校园局域网,所花的代价最小;给出任意建筑之间游历的最短路径。 基本要求: (1) 原始数据存在文件中,方便读入; (2) 建筑物点不小于20个,边不小于30个; (3) 分别用广度优先和深度优先的方法遍历图,起始点定为1号教学楼; (4) 建立校园局域网,要求所花的代价最小; (5) 查询从1号教学楼到其他各点的最短路径; (6) 查询图中任意两个建筑间的最短路径。 4、Hash表应用 任务 :设计散列表实现电话号码查找系统。 基本要求: 1) 设每个记录有下列数据项:电话号码、用户名、地址; 2) 从键盘或文件输入各记录,不少于30个,以电话号码为关键字建立散列表; 3) 采用链地址的方法解决冲突; 4) 查找并显示给定电话号码的记录; 5、排序算法比较 任务 :利用随机函数产生10个样本(其中之一已为正序,之一为倒序),每个样本有20000随机整数,利用直接插入排序、希尔排序,冒泡排序、快速排序、选择排序、堆排序,归并排序(递归和非递归),基数排序八种排序方法进行排序(结果为由小到大的顺序),并统计每一种排序所耗费的平均时间 二、选做题。 1、 运动会分数统计 任务:参加运动会有n个学校,学校编号为1……n。比赛分成m个男子项目,和w个女子项目。项目编号为男子1……m,女子m+1……m+w。不同的项目取前五名或前三名积分;取前五名的积分分别为:7、5、3、2、1,前三名的积分分别为:5、3、2;哪些取前五名或前三名由学生自己设定。(m=10 , w=8 , n=15) 功能要求: 1).可以输入各个项目的前三名或前五名的成绩; 2).能统计各学校总分(用链表); 3).可以按学校编号、学校总分、男女团体总分排序输出(快速、基数); 4).可按学校编号查询学校某个项目的情况;可按项目编号查询前三或前五名的学校。 界面要求:有合理的提示,每个功能可以设立菜单,根据提示,可以完成相关的功能要求。 存储结构:学生自己根据系统功能要求自己设计,但是要求运动会的相关数据要存储在数据文件中。 测试数据:要求使用1、全部合法数据;2、局部非法数据。进行程序测试,以保证程序的稳定。测试数据及测试结果请在上交的资料中写明; 2、 迷宫求解 任务:可以读入一个任意大小的迷宫数据,分别用广度和深度搜索的方法求出一条走出迷宫的路径,并将路径输出(最佳路径); 要求:以较为直观的方式显示结果 3、 Huffman编码 任务 :对一篇英文文章,统计各字符出现的次数,实现Huffman编码; 要求:输出每个字符出现的次数和编码,其中求最小权值要求用堆实现; 4、营业窗口队列模拟 任务:实现具有n(n=3)个窗口的现实队列模拟,统计每人的等待时间。 要求: 1). 随机产生顾客的到达时间和服务时间存盘。 2). 利用存盘数据实现队列的插入和删除。 2). 当有顾客离开时,根据队列长度调整队尾。 3). 考虑顾客中途离队的情况。 4). 考虑顾客具有优先级的情况。 5、公交线路提示 任务:建立南京主要公交线路图。 要求:输入任意两站点,给出最佳的乘车线路和转车地点。 路线信息可上网查询 6、家谱管理系统 任务:实现具有下列功能的家谱管理系统 功能要求: 1). 输入文件以存放最初家谱中各成员的信息,成员的信息中均应包含以下内容:姓名、出生日期、婚否、地址、健在否、死亡日期(若其已死亡),也可附加其它信息、但不是必需的。 2). 实现数据的存盘和读盘。 3). 以图形方式显示家谱。 4). 显示第n 代所有人的信息。 5). 按照姓名查询,输出成员信息(包括其本人、父亲、孩子的信息)。 6). 按照出生日期查询成员名单。 7). 输入两人姓名,确定其关系。 8). 某成员添加孩子。 9). 删除某成员(若其还有后代,则一并删除)。 10).修改某成员信息。 11).按出生日期对家谱中所有人排序。 12).打开一家谱时,提示当天生日的健在成员。 要求:建立至少30个成员,以较为直观的方式显示结果,并提供文稿形式以便检查。 界面要求:有合理的提示,每个功能可以设立菜单,根据提示,可以完成相关的功能。 存储结构:学生自己根据系统功能要求自己设计,但是要求相关数据要存储在数据文件中。测试数据:要求使用1、全部合法数据;2、局部非法数据。进行程序测试,以保证程序的稳定。测试数据及测试结果请在上交的资料中写明; 7、算术表达式求值 任务: 一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正整数,运算符只含加减乘除等四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:#(7+15)*(23-28/4)#。引入表达式起始、结束符是为了方便。编程利用“算符优先法”求算术表达式的值。 要求: (1) 从键盘读入一个合法的算术表达式,输出正确的结果。 (2) 显示输入序列和栈的变化过程。 8、电子小字典 任务:建立一个微型电子字典,实现生词的加入,单词的查找、删除,修改等操作。 数据结构:键树 9、稀疏矩阵相乘 任务:以三元组形式存储稀疏矩阵,实现矩阵相乘 10、平衡二叉树 任务:平衡二叉树的建立、结点的插入和删除。 11、B-树 任务:3阶B-树的结点的插入和删除。 12、编写“连连看”程序。 13、……(自选合适的题目) 成绩评定细则:(优、良、中、及格、不及格五等级) 1. 正确性:程序是否可以运行,结果是否正确(20%) 2. 功能的完备性:是否实现要求的所有子功能(20%) 3. 课程设计报告中的算法说明,课程设计报告中总结(20%) 4. 独立完成情况( 40%) 加分项目: 1.工作量和选题难度 2.可读性:代码编写是否规范,是否便于阅读。如函数、变量命名,‘{ }’的缩进,关键位置适量注释等 3.功能的完善:除要求实现的功能外,完成了其它的功能,实现了功能的完善 4.健壮性:异常处理的情况 5.界面的设计:可视化界面,或者交互良好的DOS界面 6. ……(自荐加分项目) 代码量要求:>=2500行。 代码总量 = 课设题目1 代码量 + 课设题目2 代码量…… 若代码总量低于2500行,则成绩按比例打折。 编程语言:C或C++语言 编程环境:Microsoft Visual C++ 6.0 检查方式:一对一上机检查 总体上检查程序的代码量,正确性,可读性,健壮性,功能的完备性,程序的结构是否合理;根据实际情况进行详细的程序代码检查。 时间安排: 1 上机时间安排 2课程设计检查时间 3 课程设计报告上交时间 课程设计报告要求: 1.课程设计报告封面:包括课题名称、班级、学号、学生姓名、成绩和指导教师; 2.课程设计报告目录:每部分内容所在页码; 3.需求分析:给出每道题的需求; 4.概要设计:给出每道题采用的数据结构,算法设计思想,算法的时间复杂度; 5.详细设计:给出每道题的源程序,并在必要的代码处给出注释; 6.功能测试:给出每道题的测试数据和结果; 7.完成情况:每道题完成部分和未完成部分,自己最满意的部分; 8.代码量:每道题代码的行数和总行数; 9.心得体会:包括课程设计设中遇到的问题,如何解决,编程的体验,感想和建议; 10.课程设计报告的电子文档在检查后一周内上交班长。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值