树的基本术语

概念就是计算机科学最精华的部分,所谓的概念就是解决问题的思路。(来自知乎网友)

一定有规律,一定有用途。


         数据结构中,概念众多,非常复杂,之前认为没有用,让理解变的复杂,对于之前刚接触的我来说也的确如此(因为不知道怎么用,不知道是什么),但是越学越多发现这些概念的作用愈来愈大,它细分并且整合了知识点,让内容变得又条理。

        此为树的第二弹,第一弹为初识树(二叉树,堆,并查集)-优快云博客,相同内容不再重复介绍。        


树的基本术语

  • 结点的度:结点拥有的子树数称为结点的度。如A的度为,B的度为1,C是0,D是2。
  • 层次:根为第一层,子树为第二层,依次类推。
  • 树的深度:就是树的最大层数,另外还有结点的深度就是该结点所在的层数。

        答案为:5. 


二叉树的性质

        二叉树两个比较重要的东西是结点

        如果有层为n的二叉树,那么它的结点个数最多(满二叉树)为2^n - 1​,理解就是:2^{n-1}+2^{n-1} -1​。前n-1层有2^{n-1} -1​,第n层有2^{n-1}​个。

        我们可以把二叉树与二进制联系在一起,我们算一个层次为5的二叉树的个数,就是五位二进制全为1,即为11111 。计算它,就是一个二进制的规律,第五层为2^5​=32, 那么前4层相加就是32-1==31,总共就是63。(我认为这样算比较明了,具体看自己)


二叉树的存储结构

        顺序存储

        之前我们用的一维数组的存储方法就是顺序存储,它的优点和缺点都是顺序。

       如果是一个完全二叉树,那么用顺序存储方便又快捷。 但是如果是一个单支树(如下图):

        只有A、B、C三个结点,但会用长度为7的数组。故我们经常用的是链式存储。

        链式存储

           顾名思义就是用链表进行存储,我们知道一个二叉树由一个数据元素和分别指向其左右子树的两个分支构成,所以表示二叉树的链表至少包含三个域:数据域和左右两个指针域。这是二叉链表;如果再增加一个指向双亲结点(父节点)的指针域,那就是三叉链表

        二叉链表结构体:

// 定义节点结构体
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}//初始化表
};

遍历二叉树

        对于顺序存储的二叉树,就是通过下标直接遍历,但对于链式存储来说,对于树这种非线性结构,我们通过遍历来进行线索化,我们一般按三种遍历方法来区分:前序遍历、中序遍历和后序遍历。

        简单来说,每种遍历都可以分三步(类似于汉诺塔):

前序遍历

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

中序遍历

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

后序遍历

  1. 后序遍历左子树
  2. 后序遍历右子树
  3. 访问根节点
void Postscript(TNode* T)/*后序遍历*/
{
	if (T != NULL)
	{
		Postscript(T->left);	// 遍历结点左子树
		Postscript(T->right);	// 遍历结点右子树
		printf("%d ",T->data);	// 访问结点
    } 
}

与排序算法一样,三种遍历方法也有各自的优缺点:

        通过二叉树,我们可以推出它的前中后序遍历,同样我们根据前中序表达式也能的到二叉树。 在二叉树中,通过先序遍历和中序遍历序列,或者中序遍历和后序遍历序列,可以唯一确定一棵二叉树。

 二叉树的重构

  • 已知先序遍历和中序遍历序列,可以重构二叉树。例如,先序遍历序列为 [A, B, D, E, C, F],中序遍历序列为 [D, B, E, A, F, C],可以通过先序找到根节点,再通过中序划分左右子树来重构二叉树。
  • 已知中序遍历和后序遍历序列,也可以重构二叉树。例如,中序遍历序列为 [D, B, E, A, F, C],后序遍历序列为 [D, E, B, F, C, A],可以通过后序找到根节点,再通过中序划分左右子树来重构二叉树。

         但是对于一般的树(非二叉树),情况有所不同。  对于一般的树,仅通过先根遍历和中根遍历序列,或者中根和后根遍历序列,不能唯一确定一棵树。因为一般树的节点可能有多个子节点,不像二叉树只有左右子节点,在重构过程中会出现多种可能的树结构。

        森林的中序(先序)遍历次序与其转换得到的二叉树的中序(先序)遍历次数相同树的后根遍历次序与其转换得到的二叉树的后序遍历次序不同

线索二叉树

        我们在动态遍历的过程,每个结点有且仅有一个前驱结点和后继结点,通过这些结点,我们可以将二叉树变为线索化,要想实现它,第一个可能会想到加两个指针域,如果你推一边会发现,二叉树的前驱结点或后继结点域左子树或右子树有重复的。这样就导致了空间的浪费。

        我们知道由n个结点构成的二叉树由2n个指针域,在构造二叉树时会使用n-1个,剩下的n+1个就是空链域,因此我们就可以使用这些空链域来存放结点的前驱和后继信息。

        我们可做如下规定,若结点有左子树,则其lchild域指示其左孩子,否则令lchild域指示其前驱;若结点有右子树,则其rchild域指示其右孩子,否则令其rchild指示其后继。我们需要增加两个标志域(如0和1)。

  

若LTag=0,则lchild域指示结点左孩子,若LTag=1,则lchild域指示其结点的前驱。

若RTag=0,则rchild域指示结点右孩子,若RTag=1,则child域指示其结点后继。

         以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针,叫做线索。加上线索的二叉树称为线索化二叉树。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化


树的存储结构:

双亲表示法

        双亲表示法通过记录每个节点的父节点来表示树的结构。

  

孩子表示法

        孩子表示法通过记录每个节点的子节点来表示树的结构。

  

孩子兄弟表示法(二叉链表)

        即二叉链表和三叉链表,需要先将一般树转化为二叉树。每个节点记录其第一个孩子节点和下一个兄弟节点。不是左右子树

  

  


森林和二叉树的转换      

  森林和二叉树之间可以相互转换,这种转换通常基于孩子兄弟表示法。通过将森林中的每棵树转换为二叉树,再将多棵二叉树合并,可以实现森林到二叉树的转换;反之,通过将二叉树拆分为多棵二叉树,再将每棵二叉树转换为树,可以实现二叉树到森林的转换。        

森林转换为二叉树

森林是由多棵树组成的集合。将森林转换为二叉树的步骤如下:

转换规则:

  1. 将每棵树转换为二叉树

    • 使用孩子兄弟表示法,将每棵树的节点转换为二叉树的节点:

      • 每个节点的左指针指向其第一个孩子。

      • 每个节点的右指针指向其下一个兄弟。

  2. 将多棵二叉树合并为一棵二叉树

    • 将第一棵二叉树的根节点作为最终二叉树的根节点。

    • 将第二棵二叉树的根节点作为第一棵二叉树根节点的右子树。

    • 将第三棵二叉树的根节点作为第二棵二叉树根节点的右子树,依此类推。

二叉树转换为森林

将二叉树转换为森林的步骤如下:

转换规则:

  1. 将二叉树拆分为多棵二叉树

    • 从根节点开始,沿着右指针(兄弟指针)拆分二叉树。

    • 每棵二叉树的根节点是原二叉树中右指针链上的一个节点。

  2. 将每棵二叉树转换为树

    • 使用孩子兄弟表示法的逆过程,将二叉树的节点转换为树的节点:

      • 每个节点的左指针指向其第一个孩子。

      • 每个节点的右指针指向其下一个兄弟。

二叉排序树

  •  左子树不为空,则左子树上的所有结点的值均小于它的根结点。
  • 右子树不为空,则右子树上的所有结点的值均大于它的根结点。
  • 它的左、右子树分别为二叉树。
  • 二叉排序树是递归定义的。由定义可以得出二叉排序树的一个重要性质:中序遍历一颗二叉树时可以得到一个结点值递增的有序数列

        当二叉排序树形似完全二叉树时,二叉排序树的效率最高,当二叉排序树是单支树时(初始序列有序时),对二叉排序树的查找退化为对有序的单链表的顺序查找。

二叉搜索树

        一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,

  • 其左子树中所有结点的键值小于该结点的键值;
  • 其右子树中所有结点的键值大于等于该结点的键值;
  • 其左右子树都是二叉搜索树。

例题

        L2-004 这是二叉搜索树吗? - 团体程序设计天梯赛-练习集

解析:

参考文献

        数据结构(C语言版) IBSN为978-7-115-57666-8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值