数据结构与算法--树

05 树

5.1 树的定义和术语

(1)树是一类重要的 非线性 数据结构,是以分支关系定义的层次结构。

(2)几个概念:

  • 结点的度:结点的子树数

  • 树的度:一棵树中最大的结点度数

  • 深度:树中结点的最大层次数

(3)二叉树

性质:

  • 每个结点至多有两个子树,且结点有左右子树之分;

  • 二叉树的第 i i i 层至多有 2 i − 1 2^{i-1} 2i1 个结点;

  • 深度为 k k k 的二叉树,总的结点个数至多为 2 k − 1 2^k-1 2k1

  • 对任意一棵二叉树,若其叶子结点数为 n 0 n_0 n0,度为
    2 的结点数为 n 2 n_2 n2 ,则 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1

(4)满二叉树

深度为 k k k,且结点总数为 2 k − 1 2^k-1 2k1 个。也就是说只有度为 0 和 2 的结点,不存在度为 1 的结点。

(5)完全二叉树

性质:

  • 度小于 2 的结点仅出现在最后两层,且结点要么有左右孩子,要么只有左孩子,不会出现仅有右孩子的情况;

  • 具有 n n n 个结点的完全二叉树的深度为 log ⁡ 2 n + 1 \log_2n +1 log2n+1

5.2 树型结构的存储

5.2.1 树的存储结构

(1)双亲表示法,即储存结点之间的双亲关系,也就是各结点及其双亲。

结点结构

typedef struct PTNode
{
    TElemType data;      // 数据域
    int parent;          // 双亲域
}PTNode;

树结构

#define TREE_SIZE 100      // 树结点数目

typedef struct
{
    PTNode nodes[TREE_SIZE];
    int r, n;             // 根结点的下标和结点总数
}PTtree;

初始化

PTtree T;
T.r = 0;        // 根结点位置
T.n = 9;

这样找双亲容易,找孩子难。

(2)孩子表示法:① 用一个多重链表来表示,每个结点有多个指针域,分别指向其孩子结点;② 每个结点的孩子用单链表存储,再用长度为 n n n 的数组存放每个结点的数据域及其单链表的头指针。

定义孩子结点

typedef struct CTNode
{
    int child;         // 孩子结点的位置下标
    struct CTNode *next;       
}*ChildPtr;

定义数组结点,放置结点数据和孩子结点的指针

typedef struct 
{
    TElemType data;      // 结点
    ChildPtr firstchild;
}CTBox;

定义完整树结构

typedef struct 
{
    CTBox nodes[TREE_SIZE];
    int r, n;         // 根结点下标和结点总数
}

但是这种表示法找孩子容易,找双亲难。

(3)带双亲的孩子链表:结合双亲表示法和孩子表示法

(4)孩子-兄弟表示法:储存该结点第一个孩子的位置和兄弟结点的位置

定义结点结构

typedef struct CSnode
{
    ElemType data;         // 数据域
    struc CSnode *firstchild, *nextsibling;   // 指向第一个孩子和下一个兄弟结点
}CSnode, *CSTree;
5.2.2 二叉树的存储结构

(1)顺序存储结构,即按层次遍历,将元素依次放入数组中。

(2)二叉链表表示法,每个结点包括数据域及其左右孩子的指针

typedef struct BiTNode
{
    TElemType data;       // 数据域
    struct BiTNode *leftchild, *rightchild;
}BiTNode, *BiTree;

这样找孩子容易,找父母难。

(3)三叉链表表示法,每个结点包括数据域及其左右孩子和双亲的指针,即一个数据域、三个指针域

typedef struct TriTNode
{
    TElemtype data;    // 数据域
    strut TriTNode *leftchild, *rightchild, *parent;
}TriTNode, *TriTree;

这样找孩子和双亲都比较容易。

5.2.3 树与二叉树的相互转换

在这里插入图片描述

(1)将树转换为二叉树

① 加线:兄弟结点之间连线;

② 抹线:对每个结点,抹去除左孩子之外的其他孩子之间的连线;

③ 以树的根结点为轴心,将整树顺时针转 45°。

注意:树转换得到的二叉树右子树一定为空。

(2)将二叉树还原成树

① 加线:连祖孙,若 p 是双亲的左孩子,则将 p 的右孩子,右孩子的右孩子… 与 p 的双亲结点相连;

② 抹线:断父子,抹掉原二叉树中双亲与右孩子之间的连线;

③ 将结点按层次排列,形成树结构。

注意:只能将右子树为空的二叉树还原成树

5.2.4 森林和二叉树的相互转换

(1)将森林转换为二叉树

① 将各棵树分别转换成二叉树;

② 将每棵树的根结点用线相连;

③ 以第一棵树的根结点为二叉树的根,再以根结点为轴心,顺时针旋转,构成二叉树的结构。

注意:森林转换成的二叉树右子树非空。

(2)将二叉树还原成森林

① 抹线:将二叉树中根结点与其右孩子连线,及沿右分支找
到的所有右孩子间连线全部抹掉,使之变成孤立的二叉树;

② 还原:将孤立的二叉树分别还原成树。

注意:右子树不为空的二叉树只能还原成森林。

5.3 树型结构的遍历

5.3.1 二叉树的遍历

(1)先序遍历

void PreOrderTraverse(BiTree T)
{
    if(T)
    {
        printf("%c\t", T->data);
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}

(2)中序遍历

void InOrderTraverse(BiTree T)
{
    if(T)
    {
        PreOrderTraverse(T->lchild);
        printf("%c\t", T->data);
        PreOrderTraverse(T->rchild);
    }
}

(3)后序遍历

void PostOrderTraverse(BiTree T)
{
    if(T)
    {
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
        printf("%c\t", T->data);
    }
}

(4)层次遍历

5.3.2 树的遍历

(1)先根遍历:先访问树的根结点,然后依次先根遍历根的每棵子树

先根遍历树等价于先序遍历对应的二叉树

(2)后根遍历:先依次遍历每棵子树,然后访问根结点

后根遍历树等价于中序遍历对应的二叉树

(3)层次遍历

5.4 二叉树的应用

5.4.1 哈夫曼树

哈夫曼树:带权路径长度最短的树

注意:每次都在森林中选根结点值最小的树构造新的二叉树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值