hnu 数据结构 实验四

实验4 二叉树的应用

分别提交两个附件:
1:设计文档(提交PDF格式的文件,文件名要规范,参看规范)。
2:实验源码包。(文件名也要规范,可以参考报告文件的格式)

一、问题分析
要处理的对象:N个不同的非负键值整数数列
要实现的功能:利用这个非负键值整数数列,构造一颗既是CBT也是BST的树,并将这个数以层次遍历的形式在一行中输出。
结果显示:以层次遍历的形式输出这棵树,输出要求每个数字必须由一个空格隔开,且行首和行尾没有多余的空格。

二、算法思想设计
① 这个实验的首要任务是建立这样一棵既是二叉搜索树,又是完全二叉树的树,所以这棵树要尽可能的矮,且第k层的所有节点都连续集中在最左边。
② 构造这棵树的初步思想是使用递归,主要分三步:
a.先找到根节点的值;
b.再确定这个根节点的左子树和右子树;
c.接着分别以左子树和右子树作为新的根节点,分别找寻他们的左子树和右子树;
d.循环递归,直到找完所有的数值,完成建立树。
③ 从上述建树的过程可以看出,建树的关键是找到数组中各个根节点。下面的步骤将围绕根节点的寻找展开。
④ 寻找根节点的方法:将数组进行从小到大的顺序排序,只需要确定了左子节点的个数,就能确定根节点的位置。
⑤ 确定左子节点个数的方法:因为该树为CBT,故从1-(k-1)层一定是一个满二叉树(也是完全二叉树),所有节点个数加起来为(-1)个,
a.先确定树的层数。因为该树为CBT,故层数k满足-1≧N,解得最小k即为层数。
b.因为该树是CBT,故从1到(k-1)层一定是一个满二叉树(也是完全二叉树),从1到(k-1)层,节点个数加起来为(-1)个,则最后一层的叶子结点数为N-(-1)个。
c.最后一层的满值个数是,若b中算出来最后一层的叶子结点个数>/2,则左子节点数最后一层为/2个,否则则为N-(-1)个。
⑥ 数组个数出去最小的几个左子节点数外,其余的数对半分,中间的值即为根节点。
⑦ 根节点找到后数组分为左右两个子段数组,利用递归循环,重复上述寻找根节点的步骤,完成树的建立。

三、样例分析
【样例】10
1 2 3 4 5 6 7 8 9 0
【分析】
① 数组先进行从小到大的排序,为0 1 2 3 4 5 6 7 8 9;
② 数组长度N=10,由-1=15≧N,算得k=4,一共4层。由N-(-1)得最后一层的叶子结点数为3,3</2=8,故最后一层左子节点数为3;
③ 数组忽略前三个最小的数,后续7个数取中间的数6作为第一层的根节点,并将6的左段作为【数组1】(0 1 2 3 4 5),右端为【数组2】(7 8 9);
④ 在左端对【数组1】进行递归,此时N=6,由-1=7≧N,算得k=3,一共3层。由N-(-1)得最后一层的叶子结点数为3,3</2=2,故最后一层左子节点数为2。忽略【数组1】中前两个数和后一个数,2 3 4三个数取中间值为3作为左子节点。
⑤ 以3为新的根节点,将【数组1】分为【数组11】(0 1 2)和数组【数组12】(4 5);
⑥ 对【数组11】进行递归,此时N=3,由-1≧N,算得k=2,一共2层。由N-(-1)得最后一层的叶子结点数为2,2>/2=1,故最后一层左子节点数为1。忽略【数组11】中前1个数和后1个数,根节点为1;
⑦ 以根节点1为分界点,左端0右端2,再根据递归作为1的左子节点和右子结点。
⑧ 返回结点3,结点3的右端【数组12】(4 5),根据递归可得3的右子节点为5,5的左子节点为4
⑨ 返回结点6,处理6的右端【数组2】(7 8 9),根据递归,一共2层,最底层的左子节点数为1,根节点为8;
⑩ 根据根节点8将【数组2】分为7和9两个数组,根据递归得8的左子节点为7,右子结点为9.
⑪ 递归到这里,用完了所有数字,完成建树。树的形状如下图:
在这里插入图片描述

【样例输出】
6 3 8 1 5 7 9 0 2 4

四、关键功能的算法步骤
① 对输入的数组进行排序sort(number,number+n);
② 计算树的层数
③ 最底下一层的左子节点个数为left_number = n-(key/2)+1;left_number=(left_number>key/4)?key/4:left_number;
④ 确定树的高度key += pow(2,i);
⑤ 分别对左右部分进行递归root->setLeft(built_CBT_BST(leftinorder,rt_pos,k - 1));;root->setRight(built_CBT_BST(rightinorder,n - rt_pos - 1,k - 1));
⑥ 层次输出树myTree.levelOrder(root);

五、分析
1)Cin>>n;时间复杂度为1;
2)for(int i = 0 ; i < n ; i++) {cin >> number[i];},在数组中依次存储数据,时间复杂度为n;
3)数组排序sort(number,number+n);时间复杂度为log n;
4)创建树build_BST_CBT(number[],n,k);时间复杂度为n;
5)输出树,时间复杂度为n;
6)总的时间复杂度为O(n);
7)计算过程中给每个节点开辟了空间,故空间复杂度为O(n)

六、二叉树ADT的实现
① void clear()=0;//清空二叉树
② void preOrder(void(*visit)(BinNode*node))=0;//前序遍历
③ void inOrder(void(*visit)(BinNode*node))=0;//中序遍历
④ void postOrder(void(*visit)(BinNode*node))=0;//后序遍历
⑤ void LevelOrderTranverse(void(*visit)(BinNode*node))=0;//层次遍历
⑥ bool create_by_post_and_in_order(E post[],E in[],int n)=0;//按照中序遍历和后序遍历的结果还原树
⑦ int BinTreeDepth(BinNode*tmp)=0;//计算树的深度
⑧ int BinTreeNodes(BinNode*tmp)=0;//计算树的结点数
⑨ int BinTreeHeight(BinNode*tmp)=0;//计算树的高度
⑩ int BinTreeLeafs(BinNodetmp)=0;//计算树的叶子结点数
⑪ bool find(BinNodetmp,E e)=0;//在树中寻找值e
⑫ int countNode(BinNode
rt)
⑬ BinNode
built_CBT_BST(int number[],int n,int k)//按照一组数建立一个即是BST又是CBT的树

代码实现部分:
BinNode.h

#ifndef BINNODE_H_INCLUDED
#define BINNODE_H_INCLUDED

#include <iostream>
#include <math.h>
using namespace std;

template<typename E>
class BinNode//结点类
{
private:
    BinNode*lc;//左孩子
    BinNode*rc;//右孩子
    E elem;

public:

    BinNode()//默认构造函数
    {
        lc = NULL;
        rc = NULL;
    }
    BinNode(E tmp,BinNode*l=NULL,BinNode*r=NULL)//带参构造函数
    {
        elem = tmp;
        lc = l;
        rc = r;
    }
    BinNode*left()//返回左孩子
    {
        return lc;
    }
    BinNode*right()//返回右孩子
    {
        return rc;
    }
    void setLeft(BinNode*l)//设置左孩子
    {
        lc = l;
    }
    void setRight(BinNode*r)//设置右孩子
    {
        rc = r;
    }
    void setValue(E tmp)//设置当前结点的值
    {
        elem = tmp;
    }
    E getValue()//获得当前结点的值
    {
        return elem;
    }
    bool isLeaf()//判断当前结点是否为叶子结点
    {
        if(lc == NULL&&rc == NULL)
            return true;
        else
            return false;
    }
};

#endif // BINNODE_H_INCLUDED

BinTree.h

#ifndef BINTREE_H_INCLUDED
#define BINTREE_H_INCLUDED

#include "BinNode.h"
#include "string"
#include <queue>

template<typename E>
class BinTree //二叉树类
{
private:
    BinNode<E>*root;//根节点

    bool build_BST_CBT_help(BinNode<E>* now,E number[],int start,int endd)
    {
        int n = endd - start + 1;
        int k=1;int key =2;
        while(n+1>key)
        {
            k++;
            key *= 2;
        }
        int left_number = n-(key/2)+1;
        left_number=(left_number>key/4)?key/4:left_number;
        int address = start+left_number+(n-left_number-1)/2;
        E temp=number[address];
        root->setValue(temp);

        if(address>0)//有左子树
        {
            root->setLeft(new BinNode<E>);
            if(!build_BST_CBT_help(root->left(),number,address-(n-left_number-1)/2-left_number,address-1)) return 0;
        }
        if(address<n-1)//有右子树
        {
            root->setRight(new BinNode<E>);
            if(!build_BST_CBT_help(root->right(),number,address+1,address+(n-left_number-1)/2)) return 0;
        }
        return 1;
    }
    bool create_by_post_and_in_order_help(BinNode<E>* now,E post[],E in[],int pi,int pj,int ini,int inj)
    {
        E temp=post[pj];
        int pos=ini;
        for(pos=ini;pos<inj;pos++)
        {
            if(in[pos]==temp)
            {
                break;
            }
        }
        if(pos>inj)
        {
            return 0;
        }
        now->setValue(temp);
        if(pos>ini)//有左子树
        {
            int num_of_left=pos-ini;
            now->setLeft(new BinNode<E>);
            if(!create_by_post_and_in_order_help(now->left(),post,in,pi,pi+num_of_left-1,ini,pos-1)) return 0;
        }
        if(pos<inj)//有右子树
        {
            int num_of_right=inj-pos;
            now->setRight(new BinNode<E>);
            if(!create_by_post_and_in_order_help(now->right(),post,in,pj-num_of_right,pj-1,pos+1,inj)) return 0;
        }
        return 1;

    }
    void clear(BinNode<E>*r)//清空二叉树
    {
        if(r==NULL)
            return ;
        if(r->left()!=NULL)
        {
            clear(r->left());
        }
        if(r->right()!=NULL)
        {
            clear(r->right());
        }
        delete r;
        r = NULL;
    }
    void preOrder(BinNode<E>*tmp,void(*visit)(BinNode<E>*node))
    //先序遍历,void(*visit)(BinNode<E>*node)为一个函数指针参数
    {
        if(tmp== NULL)
            return;
        visit(tmp);
        preOrder(tmp->left(),visit);
        preOrder(tmp->right(),visit);
    }
    void inOrder(BinNode<E>*tmp,void(*visit)(BinNode<E>*node))
    //中序遍历,void(*visit)(BinNode<E>*node)为一个函数指针参数
    {
        if(tmp == NULL)
            return;
        inOrder(tmp->left(),visit);
        visit(tmp);
        inOrder(tmp->right(),visit);
    }
    void postOrder(BinNode<E>*tmp,void(*visit)(BinNode<E>*node))
    //后续遍历,void(*visit)(BinNode<E>*node)为一个函数指针参数
    {
        if(tmp == NULL)
            return;
        postOrder(tmp->left(),visit);
        postOrder(tmp->right(),visit);
        visit(tmp);
    }
    void LevelOrderTranverse(BinNode<E>*tmp,void(*visit)(BinNode<E>*node))
    //层次遍历
    {
        if(tmp ==NULL)
            return;
        queue<BinNode<E>*>que;
        que.push(tmp);
        BinNode<E>*curr;
        //int sum=0;
        while(que.empty()!=true)
        {
            curr = que.front();
            if(curr->left()!=NULL)
            {
                que.push(curr->left());
            }
            if(curr->right()!=NULL)
            {
                que.push(curr->right());
            }
            que.pop();
            visit(curr);
        }
    }

    int BinTreeDepth(BinNode<E>*tmp)
    //获得二叉树的深度
    {
        if(tmp==NULL)
            return 0;
        int ld=0;
        int rd=0;
        if(tmp->left!=NULL)
            ld=BinTreeDepth(tmp->left());
        if(tmp->right!=NULL)
            rd=BinTreeDepth(tmp->right());
        if(ld>=rd)
            return ld+1;
        else
            return rd+1;
    }
    int BinTreeNodes(BinNode<E>*tmp)
    //获得二叉树的结点数
    {
        if(tmp==NULL)
            return 0;
        queue<BinNode<E>*>que;
        que.push(tmp);
        BinNode<E>*curr;
        int sum = 0;
        while(que.empty!=true)
        {
            curr = que.front();
            if(curr->left()!=NULL)
                que.push(curr->left());
            if(curr->right()!=NULL)
                que.push(curr->righ());
            que.pop();
            sum++;
        }
        return sum;
    }
    int BinTreeHeight(BinNode<E>*tmp)
    //获得二叉树的高度
    {
        if(tmp==NULL)
            return 0;
        return BinTreeDepth(tmp)-1;
    }
    int BinTreeLeafs(BinNode<E>*tmp)
    //获得二叉树的叶子结点数
    {
        if(tmp==NULL)
            return 0;
        queue<BinNode<E>*>que;
        que.push(tmp);
        BinNode<E>*curr;
        int sum=0;
        while(que.empty()!=true)
        {
            curr=que.front();
            if(curr->left()!=NULL)
                que.push(curr->left);
            if(curr->right()!=NULL)
                que.push(curr->right);
            if(curr->right()==NULL&&curr->left()==NULL)
                sum++;
            que.pop();
        }
        return sum;
    }
    bool find(BinNode<E>*tmp,E e)
    //查找二叉树中是否含有某个名为e的结点
    {
        if(tmp==NULL)
            return 0;
        queue<BinNode<E>*>que;
        que.push(tmp);
        BinNode<E>*curr;
        int sum=0;
        while(que.empty()!=true)
        {
            curr=que.front();
            if(curr->left()!=NULL)
                que.push(curr->left);
            if(curr->right()!=NULL)
                que.push(curr->right);
            if(curr->getValue()==e)
                return true;
            que.pop();
        }
        return false;
    }
public:
    BinTree()//默认构造函数
    {
        root=new BinNode<E>;
    }
    ~BinTree()//析构函数
    {
        clear(root);
    }
    bool BinTreeEmpty()//判断二叉树是否为空
    {
        if(root == NULL)
            return true;
        else
            return false;
    }
    BinNode<E>*getRoot()//获得根节点
    {
        return root;
    }
    void setRoot(BinNode<E>*r)//设置根节点
    {
        root = r;
    }
    //下面的函数是对外的函数,所以内部还会有一些同名的函数,但是参数列表不一样,
    //实现数据的封装,外部的调用不会涉及到内部的数据对象
    void clear()//清空二叉树
    {
        clear(root);
        root = NULL;
    }
    void preOrder(void(*visit)(BinNode<E>*node))
    //先序遍历,传入相对应的访问函数即可对该当前结点实现不同功能的访问(输出)
    {
        inOrder(root,visit);
    }
    void inOrder(void(*visit)(BinNode<E>*node))
    //中序遍历,传入相对应的访问函数即可对该当前结点实现不同功能的访问(输出)
    {
        inOrder(root,visit);
    }
    void postOrder(void(*visit)(BinNode<E>*node))
    //后序遍历,传入相对应的访问函数即可对该当前结点实现不同功能的访问(输出)
    {
        inOrder(root,visit);
    }
    void LevelOrderTranverse(void(*visit)(BinNode<E>*node))
    //层次遍历,传入相对应的访问函数即可对该当前结点实现不同功能的访问(输出)
    {
        LevelOrderTranverse(root,visit);
    }
    int BinTreeDepth()//获得二叉树的深度
    {
        return BinTreeDepth(root);
    }
    int BinTreeNodes()//获得二叉树结点数
    {
        return BinTreeNodes(root);
    }
    int BinTreeHeight()//获得二叉树高度
    {
        return BinTreeHeigh(root);
    }
    int BinTreeLeafs()//获得二叉树叶子结点数
    {
        return BinTreeLeafs(root);
    }
    bool find(E e)//查找二叉树中是否存在名为e的结点
    {
        return find(root,e);
    }
    bool create_by_post_and_in_order(E post[],E in[],int n)
    {
        clear();
        root=new BinNode<E>;
        E temp=post[n-1];
        root->setValue(temp);
        int pos=0;
        for(pos=0;pos<n;pos++)
        {
            if(in[pos]==temp)
            {
                break;
            }
        }
        if(pos>0)//有左子树
        {
            int num_of_left=pos;
            root->setLeft(new BinNode<E>);
            if(!create_by_post_and_in_order_help(root->left(),post,in,0,0+num_of_left-1,0,pos-1)) return 0;
        }
        if(pos<n-1)//有右子树
        {
            int num_of_right=n-pos-1;
            root->setRight(new BinNode<E>);
            if(!create_by_post_and_in_order_help(root->right(),post,in,pos,pos+num_of_right-1,pos+1,pos+num_of_right)) return 0;
        }
        return 1;
    }

    bool build_BST_CBT(E number[],int n)
    {
        int k=1;int key =2;
        while(n+1>key)
        {
            k++;
            key *= 2;
        }
        int left_number = n-(key/2)+1;
        left_number=(left_number>key/4)?key/4:left_number;
        int address = left_number+(n-left_number-1)/2;
        E temp=number[address];
        root->setValue(temp);

        if(address>0)//有左子树
        {
            //int num_of_left=key;
            root->setLeft(new BinNode<E>);
            if(!build_BST_CBT_help(root->left(),number,0,address-1)) return 0;
        }
        if(address<n-1)//有右子树
        {
            //int num_of_right=n-key-1;
            root->setRight(new BinNode<E>);
            if(!build_BST_CBT_help(root->right(),number,address+1,n-1)) return 0;
        }
        return 1;
    }
};

#endif // BINTREE_H_INCLUDED

BinTree_ADT.h

#ifndef BINTREE_H
#define BINTREE_H
#include"BinNode.h"
#include<vector>
template <class E> class BinTree_ADT
{
public:
    virtual void clear()=0;
    virtual void preOrder(void(*visit)(BinNode<E>*node))=0;
    virtual void inOrder(void(*visit)(BinNode<E>*node))=0;
    virtual void postOrder(void(*visit)(BinNode<E>*node))=0;
    virtual void LevelOrderTranverse(void(*visit)(BinNode<E>*node))=0;
    virtual bool create_by_post_and_in_order(E post[],E in[],int n)=0;
    virtual int BinTreeDepth(BinNode<E>*tmp)=0;
    virtual int BinTreeNodes(BinNode<E>*tmp)=0;
    virtual int BinTreeHeight(BinNode<E>*tmp)=0;
    virtual int BinTreeLeafs(BinNode<E>*tmp)=0;
    virtual bool find(BinNode<E>*tmp,E e)=0;
    //virtual void LevelOrderTraverse(BinNode* rt)=0;
    virtual bool build_BST_CBT(E number[],int n)=0;
};
#endif // BINTREE_H

main.c

#include <iostream>
#include <string>
#include <algorithm>
#include "BinTree.h"
#include "BinNode.h"
using namespace std;

template<typename E>
void printNode(BinNode<E>*tmp)//打印结点的值的函数
{
    cout << tmp->getValue() << " ";
}

int main()
{
    int n;
    cin >> n;
    BinTree<int> tree;
    int number[n];
    for(int i = 0 ; i < n ; i++)
        cin >> number[i];
    sort(number,number+n);

    bool b ;
    b = tree.build_BST_CBT(number,n);
    if(b)
    {
        tree.LevelOrderTranverse(printNode);
    }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值