黑龙江大学《数据结构与算法》实验五 树的应用

实验五 树的应用(2 学时) 一、实验目的 1、掌握二叉树的各种存储结构的特点及适用范围。 2、掌握建立二叉树的方法。(包括顺序存储、二叉链表存储) 3、熟练掌握二叉树的前序、中序、后序遍历的递归及非递归算法;灵活运用递归的遍历 算法实现二叉树的应用。 二、实验内容 1、以二叉链表作存储结构,设计求二叉树高度的算法。 2、一棵 n 个结点的完全二叉树用向量作存储结构,用非递归算法实现对该二叉树进行前 序遍历。 3、以二叉链表作存储结构,编写非递归的前序、中序、后序遍历算法。 三、实验指导 1、题目 1 和题目 3 要求事先建立好二叉树(以二叉链表作存储结构);题目 2 要求事先 建立好二叉树(采用顺序存储结构)。 2、提示 (1)题目 1 可参考二叉树的递归遍历算法进行设计。 (2)题目 2 考虑是否可以借助其他辅助结构进行设计。 (3)题目 3 可以考虑利用栈来实现,难点在于后序遍历的非递归算法的实现(何时入栈, 如何标识何时出栈)。 四、考核标准 1、至少完成 2 个题目,设计合理,结果正确;评定为 A 2、完成部分题目或未按时完成,设计比较合理,结果正确;评定为 B 或 C 3、未独立完成实验要求;评定为 D (本单元共 3 分。只完成前两个题目最多得 2 分,高质量完成 3 个题目给 3 分,未按时 完成或算法的时间与空间复杂度过高会酌情扣分。)

蒟蒻第一次发文,这个实验比较简单,大概没什么要特别注意的地方,我自己测试也没什么问题,大概,有问题跟我说,我大概会改,大概

 

f8810a2bf47941459acb9b2e981467d4.png

 

 

#include <iostream>
#include<bits/stdc++.h>

using namespace std;

int hight;//全局变量用于记录树的最大层数

typedef struct tree1//树的节点构造体
{
    int date;//树中存的数据类型为int类型
    struct tree1*lchild;//树的左孩子节点
    struct tree1*rchild;//树的右孩子节点
}Tree1;


void treebuild1(Tree1**node)//树的建立
{
    char da;//用字符形式储存输入的数据方便用于判断是否将节点设置为空
    da=getchar();//输入字符
    if(da=='n')//判断节点是否为空,如果要设定节点为空,就输入字符n
        *node=NULL;//如果输入为字符n就设定为空
    else//否则
    {
        *node=new Tree1;//给节点分配空间
        (*node)->date=da-'0';//将字符转换成整数并存入节点的数据里
        treebuild1(&((*node)->lchild));//继续遍历左孩子节点
        treebuild1(&((*node)->rchild));//继续遍历右孩子节点
    }
}

void bl(Tree1*node)//前序遍历二叉树(用于判断二叉树是否构建成功)
{
    if(node!=NULL)//如果不为空就进行输出
    {
        cout<<node->date;//输出此时节点储存的数据
        bl(node->lchild);//继续往左孩子节点遍历
        bl(node->rchild);//继续往右孩子节点遍历
    }
}

void Hight(int num,Tree1*node)//计算高度
{
    if(node!=NULL)//判断节点是否是空,如果是空就不计算这个位置的层数
    {
        num++;//不为空就计算这个节点的层数
        hight=max(hight,num);//比较这个节点的层数是否大于此时记录的最大层数,如果是就更新一下
        Hight(num,node->lchild);//继续往左孩子节点遍历
        Hight(num,node->rchild);//继续往右孩子遍历
    }
}

void copytree(Tree1**a1,Tree1**a2)//树的拷贝函数
{
    *a2=new Tree1;//给拷贝的节点分配空间
    (*a2)->date=(*a1)->date;//让需要拷贝的节点的数据等于被拷贝的节点的数据
    if((*a1)->lchild!=NULL)//判断被拷贝的节点的左子树是否为空
    {
        copytree(&(*a1)->lchild,&(*a2)->lchild);//如果不为空就继续遍历被拷贝的树的左子树和需拷贝的节点的左子树
    }
    else//如果为空就把要拷贝的树的左子树设定为空
    {
        (*a2)->lchild=NULL;
    }
    if((*a1)->rchild!=NULL)//右子树同理
    {
        copytree(&(*a1)->rchild,&(*a2)->rchild);
    }
    else
    {
        (*a2)->rchild=NULL;
    }
}

int main()
{
    cout<<"建立树:";
    Tree1 *tree=NULL;//创建根节点
    treebuild1(&tree);//构建二叉树
    cout<<"输出树中的元素:";
    bl(tree);//测试二叉树是否构建成功(其实不测试也行......大概)
    cout<<endl;
    int x=0;//每个节点的层数的初始值为零
    hight=0;//最大层数的初始值为零
    cout<<"树的高度为:";
    Hight(x,tree);//先序递归遍历树求最大层数
    cout<<hight<<endl;
    int a[100];//用来线性存树的数组,说要用向量,其实我感觉都行
    bool b[100];//用来判断当前节点是否走过
    int n;//记录存入节点的数量
    cout<<"输入非递归前序遍历用的树的节点数量:";
    cin>>n;
    cout<<"输入节点:";
    for(int i=1;i<=n;i++)cin>>a[i];//输入要存的节点
    char ssss;//用来存换号格,下面还用建立个树,这里输入完之后换行会影响
    scanf("%c",&ssss);//换行
    cout<<"输出这个树的前序递归:";
    int k=1;//记录当前节点在线性表的位置
    memset(b,true,sizeof(b));//每个节点还未被遍历,初始值设定为true
    for(int i=1;i<=n;i++)
    {
        cout<<a[k]<<" ";//输出此时的节点
        b[k]=false;//将此时的节点设定为已经遍历过
        if(k*2<=n&&b[k*2])k*=2;//如果当前节点的左子节点没有遍历过,就优先遍历左子节点
        else//判断如果已经遍历过了或者不存在
        {
            if(k+1<=n&&b[k+1]&&(k+1)%2!=0)k++;//判断这个节点上一个节点的右子节点是否遍历,没有就遍历
            else//如果都遍历过了
            {
                while(!b[k/2])//就往上查找
                {
                    k/=2;
                    if((k+1)%2!=0&&b[k+1])//找到没遍历过的右节点,就往右遍历
                    {
                        k+=1;
                        break;
                    }
                }
            }
        }
    }
    cout<<endl;
    cout<<"建立非递归用的二叉树:";
    Tree1 *T=NULL;//申请根节点
    treebuild1(&T);//构建二叉树
    Tree1*T1=NULL;
    copytree(&T,&T1);
    //这里我大致解释一下我这个复制二叉树用来干嘛,因为我这个二叉树的三个遍历方法,对二叉树的使用都是一次性的,所以就需要复制一个用来遍历。
    cout<<"进行非递归先序遍历并输出先序遍历过程:";
    Tree1*greate[100];//栈(用来存节点)
    int top=0;//栈顶位置
    top++;
    greate[top]=T1;//将二叉树入栈
    cout<<greate[top]->date<<" ";//先序遍历先输出根节点的数据
    while(top)
    {
        if(greate[top]->lchild!=NULL)//如果栈顶节点没有左孩子节点就判断他的父亲是否有右孩子,有的话就入栈并输出数据
        {
            top++;
            greate[top]=greate[top-1]->lchild;
            greate[top-1]->lchild=NULL;//链接断开防止重复走同一个节点(这就是为啥我这个用的树是一次性的原因)
            cout<<greate[top]->date<<" ";
        }
        else//如果栈顶有左孩子,将左孩子入栈
        {
            if(greate[top]->rchild!=NULL)
            {
                top++;
                greate[top]=greate[top-1]->rchild;
                greate[top-1]->rchild=NULL;//链接断开防止重复走同一个节点
                cout<<greate[top]->date<<" ";
            }
            else
            {
                top--;
            }
        }
    }
    cout<<endl;
    Tree1*T2=NULL;
    copytree(&T,&T2);//复制一个树
    cout<<"进行非递归中序遍历并输出中序遍历过程:";
    Tree1*middle[100];//存节点用的栈
    top=0;//栈顶位置
    top++;
    middle[top]=T2;//将根节点入栈
    while(top)
    {
        if(middle[top]->lchild==NULL)//如果左孩子节点为空
        {
            cout<<middle[top]->date<<" ";//输出栈顶的节点的数据
            if(middle[top]->rchild!=NULL)//输出自己节点之后再判断当前节点是否有右孩子,输出顺序是左 自己 右
            {
                middle[top]=middle[top]->rchild;//如果有就直接将自己出栈(已经输出过了),再将右孩子节点入栈
            }
            else
            {
                top--;//没有就回到自己的父亲节点,因为自己为父亲节点的左孩子节点
                if(top!=0)
                middle[top]->lchild=NULL;//断开链接防止重复输出
            }
        }
        else//如果头节点有左孩子节点就一直入左孩子节点
        {
            top++;
            middle[top]=middle[top-1]->lchild;
        }
    }
    cout<<endl;
    cout<<"进行非递归后序遍历并输出后序遍历过程:";
    Tree1*T3=NULL;
    copytree(&T,&T3);//复制一份
    Tree1*last[100];//栈
    top=0;//栈顶位置
    top++;
    last[top]=T3;//根节点入栈
    while(top)
    {
        if(last[top]->lchild==NULL&&last[top]->rchild==NULL)//自己的左右孩子节点为空说明自己为最优先输出的节点
        {
            cout<<last[top]->date<<" ";//既然自己最优先,那输出就完事了
            if(last[top-1]->rchild!=NULL)//再判断自己的父亲节点是否有右孩子,如果有就自己出栈父亲的右孩子入栈
            {
                last[top]=last[top-1]->rchild;
                last[top-1]->rchild=NULL;//断开链接防止重复
            }
            else
            {
                top--;//如果没有就出栈,已经输出过了
                if(top!=0)
                    last[top]->lchild=NULL;//断开链接防止重复
            }
        }
        else
        {
            top++;//否则就一直入左孩子节点
            last[top]=last[top-1]->lchild;
        }
    }
    cout<<endl;
    return 0;
}

 

 

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值