树和二叉树

目录

一、树的简单认识

 定义

二、二叉树

2.1 定义

2.2二叉树的基本性质

2.3二叉树的l链式存储和遍历

2.4二叉树的顺序存储和遍历

 2.5二叉树的深度


一、树的简单认识

定义

        树(tree)是 n(n ≥ 0)个节点的有限集合,当n = 0时,为空树;当n > 0 时,为非空树。人以一棵非空树,满足以下两个条件:

         1)有且仅有一个称为根的节点;

         2)除根节点以外,其余节点可以分为m( m > 0 )个互不相交的有限集T_{1},T_{2},T_{3}......T_{m},期中每一个集合本身又是一棵树,并且称为根的子树(subtree)。

 与树相关的术语比较多:

● 节点——节点包含数据元素及若干指向子树的分支信息。
节点的度——节点拥有的子树个数。
树的度——树中节点的最大度数。
● 终端节点——度为0的节点,又称为叶子。
● 分支节点——度大于0的节点。除了叶子都是分支节点。
● 内部节点——除了树根和叶子都是内部节点。
● 节点的层次——从根到该节点的层数(根节点为第1层)。
树的深度(或高度)——指所有节点中最大的层数

● 路径——树中两个节点之间所经过的节点序列。
● 路径长度——两节点之间路径上经过的边数。

● 双亲、孩子——节点的子树的根称为该节点的孩子,反之,该节点为其孩子的双亲。
● 兄弟——双亲相同的节点互称兄弟。
● 堂兄弟——双亲是兄弟的节点互称堂兄弟。
● 祖先——从该节点到树根经过的所有节点称为该节点的祖先。
● 子孙——节点的子树中的所有节点都称为该节点的子孙。

二、二叉树

 2.1 定义

         二叉树(binary tree)是n(n≥0)个节点构成的集合,它或为空树(n=0),或满足以下两个条件:
        1)有且仅有一个称为根的节点;
        2)除根节点以外,其余节点分为两个互不相交的子集T_{1}T_{2},分别称为T的左子树和右子树,且T_{1}T_{2}本身都是二叉树。


        二叉树是一种特殊的树,它最多有两个子树,分别为左子树和右子树,二者是有序的,不可以互换。也就是说,二叉树中不存在度大于2的节点。 

2.2二叉树的基本性质

性质一:二叉树的第 i 层上最多有 2^{i-1}  个节点(i ≥ 1)

性质二:一棵深度为k的二叉树中,最多有2^{k-1}个结点,最少有k个结点

性质三:在一棵二叉树中,如果叶子结点数为n_{0},度为2的结点数为n_{2},则有: n_{0}n_{2}+1。

性质四:具有n个结点的完全二叉树的深度为 \left \lfloor log2n \right \rfloor + 1

2.3二叉树的l链式存储和遍历

        (1)二叉链表:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。

template<class T>
struct Binode
{
    T data;//通用数据类型
    Binode<T>* lchild, * rchild;
}; 

        (2)二叉链表的创建:从二叉树的定义可以看出,它是递归定义的(除了根之外,左,右子树也是一棵二叉树),因此可以用递归来创建二叉树。下面将介绍的是补空法:

补空法:补空法是指如果左子树或右子树为空时,则用特殊字符补空,如“#”,然后按照根、左子树、右子树的顺序,得到先序遍历序列,根据该序列递归创建二叉树。 

算法思路:

1)输入补空后的二叉树先序遍历序列。
2)如果ch=='#', bt=NULL;否则创建一个新节点bt,令bt->data=ch;递归创建T的左子树;递归创建T的右子树。

图解:

 二叉树补空之后的先序遍历是:ABD##E##CF#G###

代码实现:(使用了模板)

Binode<T>* creat(Binode<T>* bt)
     {
        T ch;
        cin >> ch;
        if (ch == '#')
            bt = NULL;
        else
        {
            bt = new Binode<T>;
            bt->data = ch; //生成根节点
            bt->lchild = creat(bt->lchild);//递归创建左孩子
            bt->rchild = creat(bt->rchild);//递归创建右孩子
        }
        return bt;
    }

        (3)二叉树的遍历:

        二叉树的遍历就是按照某条路径搜索访问二叉树中的节点一次且仅访问一次,遍历是有顺序的,我们首先考虑二叉树的结构,按照根D,左子树L和右子树R的访问先后顺序不同有六种访问方式。如果限定先访问左子树再访问右子树,则只有三种遍历方式:先序遍历(DLR),中序遍历(LRD),后序遍历(LRD)。因为树本身是递归定义的,因此树的基本操作用递归算法比较容易实现。

先序遍历算法:

1)访问根节点;
2)先序遍历左子树;
3)先序遍历右子树。

图示:

 代码实现:

void Preorder(Binode<T>* bt)
    {//前序遍历
        if (bt != NULL)
        {
            cout << bt->data << " ";
            Preorder(bt->lchild);
            Preorder(bt->rchild);
        }
    }

中序遍历算法:

1)中序遍历左子树;
2)访问根节点;
3)中序遍历右子树。

图示:

代码:

void Inorder(Binode<T>* bt)
    {//中序遍历
        if (bt != NULL)
        {
            Inorder(bt->lchild);
            cout << bt->data << " ";
            Inorder(bt->rchild);
        }
    }

后序遍历算法:

1)后序遍历左子树;
2)后序遍历右子树;
3)访问根节点。

图示:

代码:

void Postorder(Binode<T>* bt)
{//后序遍历
        if (bt != NULL)
        {
            Postorder(bt->lchild);
            Postorder(bt->rchild);
            cout << bt->data << " ";
        }
    }

由观察可知以上三种遍历不同之处是根节点访问位置不同,那么代码上的区别就是 “cout << bt->data << " "; ”语句所在的位置不同。

        (4)层序遍历

              二叉树的遍历除一般的先序遍历、中序遍历和后序遍历这3种遍历之外,还有另一种遍历方式——层次遍历,即按照层次的顺序从左向右进行遍历

遍历过程:

( A )——(B C)——( D E F )—— ( G )

观察可得结论:一对括号作为一组, 每一组遍历是上一组所有节点的孩子,按照先左孩子再右孩子的顺序,那么我们如何实现? 这时候通常使用的是队列。

访问根节点A ,A入队

取队首元素 A ,A左右孩子不为空依次入队,并将A出队,队列元素:B C

取队首元素 B ,B左右孩子不为空依次入队,并将B出队,队列元素:C D E

取队首元素 C ,C左孩子不为空入队,并将C出队,队列元素: D E F 

取队首元素 D ,D的左右孩子为空,无元素入队,将D出队,队列元素:  E F 

……

什么时候结束呢?答案当然是:队列元素数量为0的时候啦

那废话不多说了:代码代码

void Levelorder(Binode<T>* bt)
    {//层序遍历
        Binode<T>* temp = NULL;
        queue<Binode<T>*>LeQue;
        LeQue.push(bt);//入队根节点
        while (!LeQue.empty())
        {
            temp = LeQue.front();
            cout << temp->data << " ";
            LeQue.pop();//队首元素出队
            if (temp->lchild != NULL)
                LeQue.push(temp->lchild);/左孩子不为空,左孩子入队
            if (temp->rchild != NULL)
                LeQue.push(temp->rchild);//右孩子不为空,右孩子入队
        }
        
    }

思考:如何利用层序遍历思路实现找到二叉树的最大宽度呢?(是我想偷懒了)

2.4二叉树的顺序存储和遍历

       (1) 顺序存储二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位(下标)应能体现结点之间的逻辑关系——父子关系。

        普通二叉树在顺序结构存储时,需要补充成为完全二叉树,在对应完全二叉树没有孩子的位置补充 “ ^ ” ,再进行标号。根节点对应编号1,编号后我们会发现,左孩子(L)和右孩子(R)的标号与双亲节点标号(P)之间的关系:L = 2*P,R = 2*P +1

 数组下标

  

        (2)在得到顺序存储的一维数组后,我们也来实现一下二叉树的一些基本操作:

遍历:与链式存储结构遍历的算法思路相同,只不过是将在链表上的操作转化成为在数组上实现。我们已知,前序遍历:根节点->左孩子->右孩子 ,中序遍历:左孩子->根节点->右孩子 ,后序遍历:右孩子 - >左孩子->根节点。

在遍历的算法思路不变的情况下,我们要如何做才能实现这三种遍历呢?回看链式存储时候的代码,我们发现关键的代码就在于递归访问时参数(也就是左孩子和右孩子的表示)的变化,换到这里就是找到在数组中左右孩子的表示方法。那不就是……那不就是……L = 2*P,R = 2*P +1

void PreOreder(int v)
{//前序遍历
    if (arr[v] != '^')
    {
        cout << arr[v] << " ";
        PreOreder(2 * v);
        PreOreder(2 * v + 1);
    }
}
void InOrder(int v)
{//中序遍历
    if (arr[v] != '^')
    {
        InOrder(2 * v);
        cout << arr[v] << " ";
        InOrder(2 * v + 1);
    }
}
void PosOrder(int v)
{//后序遍历
    if (arr[v] != '^')
    {
        PosOrder(2 * v);
        PosOrder(2 * v + 1);
        cout << arr[v] << " ";
    }
}

                                                        是不是很简单哇 ^_^

        那重要的问题来了——在知道基本的算法思路之后我们该如何通过给定的二叉树得到顺序结构存储的数组呢?(具体操作见代码注释叭)----------偷懒中----------

//存储下标和结构体节点
template<class T>
struct StoreN
{
    Binode<T>* Node_1;
    int index;
};
//主要代码
template<class T>
void Bitree<T>::ProInorder(Binode<T>* bt)
{
    StoreN<T>* item = new StoreN<T>;
    int j = 0;//初始化顺序数组
    for (j = 0; j < Maxn; j++)
        arr[j] = '^';

    queue<StoreN<T>*> Q;//用于寻找记录下标

    int i = 1;
    arr[i] = bt->data;//记录根节点的数据
    item->Node_1 = bt;
    item->index = i;//记录根节点对应的下标
    Q.push(item);

    while (Q.empty() != 1)
    {
        item = Q.front();
        Q.pop();//记得出队,不然陷入循环了
        if (item->Node_1->lchild != NULL)//如果左孩子不为空
        {
            StoreN<T>* templ = new StoreN<T>;//用于左孩子,一定要定义在里面
            templ->index = 2 * item->index;  //存储左孩子在数组中的下标
            templ->Node_1 = item->Node_1->lchild; //存储左孩子节点
            arr[templ->index] = templ->Node_1->data; //找到左孩子在数组中的位置
            Q.push(templ);
        }
        if (item->Node_1->rchild != NULL)//如果右孩子不为空
        {
            StoreN<T>* tempr = new StoreN<T>;//用于右孩子
            tempr->index = 2 * item->index + 1;
            tempr->Node_1 = item->Node_1->rchild;
            arr[tempr->index] = tempr->Node_1->data;
            Q.push(tempr);
        }
    }
    arr[item->index + 1] = '\0'; //输出得到的一维数组
    cout << arr + 1  << "\n";
}

 2.5二叉树的深度

链式:

int GetBTdepth(Binode<T>* bt)
    {
        int leftC = 0, rightC = 0;
        if (bt != NULL)
        {
            leftC = GetBTdepth(bt->lchild) + 1;
            rightC = GetBTdepth(bt->rchild) + 1;
        }
        return leftC > rightC ? leftC : rightC;
   }

顺序结构:

int GetBTdepth(int v)
    {
        int leftC = 0, rightC = 0;
        if (arr[v] != '^')
        {
            leftC = GetBTdepth(2*v) + 1;
            rightC = GetBTdepth(2*v+1) + 1;
        }
        return leftC > rightC ? leftC : rightC;
    }

存储完整代码:

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<queue>
using namespace std;
const int Maxn = 102;
char arr[Maxn] = { 0 };//顺序结构存储
template<class T>
struct Binode
{
    T data;
    Binode<T>* lchild, * rchild;
};
template<class T>
struct StoreN
{
    Binode<T>* Node_1;
    int index;
};
template<class T>
class Bitree
{
private:
    Binode<T>* root = NULL;
    Binode<T>* creat(Binode<T>* bt)
    {
        T ch;
        cin >> ch;
        if (ch == '#')
            bt = NULL;
        else
        {
            bt = new Binode<T>;
            bt->data = ch;
            bt->lchild = creat(bt->lchild);
            bt->rchild = creat(bt->rchild);
        }
        return bt;
    }
    void release(Binode<T>* bt)
    {
        if (bt != NULL)
        {
            release(bt->lchild);
            release(bt->rchild);
            cout << bt->data << "节点被删除" << "\n";
            delete bt;
        }

    }
    void Preorder(Binode<T>* bt)
    {//前序遍历
        if (bt != NULL)
        {
            cout << bt->data << " ";
            Preorder(bt->lchild);
            Preorder(bt->rchild);

        }
    }
    void Inorder(Binode<T>* bt)
    {//中序遍历
        if (bt != NULL)
        {
            Inorder(bt->lchild);
            cout << bt->data << " ";
            Inorder(bt->rchild);
        }
    }
    void Postorder(Binode<T>* bt)
    {//后序遍历
        if (bt != NULL)
        {
            Postorder(bt->lchild);
            Postorder(bt->rchild);
            cout << bt->data << " ";

        }
    }
    void Levelorder(Binode<T>* bt)
    {//层序遍历
        Binode<T>* temp = NULL;
        queue<Binode<T>*>LeQue;
        LeQue.push(bt);
        while (!LeQue.empty())
        {
            temp = LeQue.front();
            cout << temp->data << " ";
            LeQue.pop();
            if (temp->lchild != NULL)
                LeQue.push(temp->lchild);
            if (temp->rchild != NULL)
                LeQue.push(temp->rchild);
        }
        
    }
    int MaxBTwidth(Binode<T>* bt)
    {
        //求树的最大宽度
        Binode<T> *temp = NULL;
        int w = 0;
        int i = 0;
        queue<Binode<T>*>Que;
        Que.push(bt);
        w = 1;
        int maxw = w;
        while (!Que.empty())
        {
            temp = Que.front();
            for (i = 0; i < w; i++)
            {
                if (temp->lchild != NULL)
                    Que.push(temp->lchild);
                if (temp->rchild != NULL)
                    Que.push(temp->rchild);
                Que.pop();
            }
            w = Que.size();
            if (maxw < w)
                maxw = w;
        }
        return maxw;
    }
    int GetBTdepth(Binode<T>* bt)
    {
        int leftC = 0, rightC = 0;
        if (bt != NULL)
        {
            leftC = GetBTdepth(bt->lchild) + 1;
            rightC = GetBTdepth(bt->rchild) + 1;
        }
        return leftC > rightC ? leftC : rightC;

    }
    void GetSortArr(Binode<T>*bt);
   public:
    Bitree() { root = creat(root); }
    ~Bitree() { release(root); }
    void Preorder() { Preorder(root); }
    void Inorder() { Inorder(root); }
    void Postorder() { Postorder(root); }
    void Levelorder() { Levelorder(root); }
    void GetSortArr() { GetSortArr(root); }
    int MaxBTwidth() { return MaxBTwidth(root); }
    int GetBTdepth() { return GetBTdepth(root); }
    int DepthNodes(int item);
};
template<class T>
int Bitree<T>::DepthNodes(int item)
{
    int count = 0;
    int i = pow(2, item - 1);
    int len = pow(2, item);
    for (; i < len; i++)
    {
        if (arr[i] != '^')
        {
            cout << arr[i] << " ";
            count++;
        }
        if (arr[i] == '\0')
            return count;
    }
    return count;
}
template<class T>
void Bitree<T>::GetSortArr(Binode<T> *bt)
{
    StoreN<T>* item = new StoreN<T>;
    int j = 0;
    for (j = 0; j < Maxn; j++)
        arr[j] = '^';

    queue<StoreN<T>*> Q;

    int i = 1;
    arr[i] = bt->data;
    item->Node_1 = bt;
    item->index = i;
    Q.push(item);

    while (Q.empty() != 1)
    {
        item = Q.front();
        Q.pop();//记得出队,不然陷入循环了
        if (item->Node_1->lchild != NULL)//如果左孩子不为空
        {
            StoreN<T>* templ = new StoreN<T>;
            templ->index = 2 * item->index;       
            templ->Node_1 = item->Node_1->lchild; 
            arr[templ->index] = templ->Node_1->data;
            Q.push(templ);
        }
        if (item->Node_1->rchild != NULL)//如果右孩子不为空
        {
            StoreN<T>* tempr = new StoreN<T>;
            tempr->index = 2 * item->index + 1;
            tempr->Node_1 = item->Node_1->rchild;
            arr[tempr->index] = tempr->Node_1->data;
            Q.push(tempr);
        }
    }
    arr[item->index + 1] = '\0';
    cout << arr + 1 << "\n";
}
int main()
{
    Bitree<char>tree1;
    tree1.GetSortArr();
    cout << "\n前序遍历的结果是:\n";
    tree1.Preorder();
    cout << "\n中序遍历的结果是:\n";
    tree1.Inorder();
    cout << "\n后序遍历的结果是:\n";
    tree1.Postorder();
    cout << "\n层序遍历的结果是:\n";
    tree1.Levelorder();
    cout << "\n二叉树最大宽度是:";
    cout << tree1.MaxBTwidth() ;
    cout << "\n二叉树的深度为:";
    int BTdepth = tree1.GetBTdepth();
    cout << BTdepth << "\n";
    int v;
    cout << "你想要知道的哪一层节点:(请输入一个小于" << BTdepth << "的数)";//如果添加循环语句的话可以实现层序遍历>_>
    cout << "层节点详情是:";
    cin >> v;
    cout <<  "层节点数目是: " << tree1.DepthNodes(v)<<"\n";
    return 0;
}

<总结的时候改自己的bug实在是不易,如果有错误,还望指正,转载请声明,感谢阅读>

  

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值