跳到主要内容

树与二叉树

Start from ‎2024‎年‎4‎月‎10‎日,‏‎8:12:01

树的基本概念

祖先、子孙、双亲、孩子、兄弟、堂兄弟

  1. 祖先:头上的全部。比如说K的,EBA全是他祖先
  2. 子孙:地下的全部。比如说B的,EFKL全是子孙
  3. 双亲:头上那一个,根没有双去哪
  4. 兄弟:仅限同一个双亲的隔壁,比如说F的兄弟就是E
  5. 堂兄弟:那一层的都是

结点的度和树的度

  1. 结点的度:全部度的总和
  2. 树的度:结点的最大的度

结点的深度、高度和层次

深度从下往上数,高度从上往下数(可以从1/0开始数)

有序树和无序树

路径和路径长度

森林

由m个互不相交的树的集合组成。

树的性质

结点数=总度数+1 (n=m+1)

每个结点都有个天线,除了根没有,补个1就是总结点数

度=m的树第i层最多有$m^{i-1} $个结点

1->m->m2m^2

m0m^0->m21m^{2-1}->mi1m^{i-1}

最多结点数mi1m1\frac{m^i-1}{m-1}

用等比数列来推,

SUM=a1(1qn)1qSUM=\frac{a_1(1-q^n)}{1-q}

a1=1,q=m,在上下同*-1即可得到

SUM=mi1m1SUM=\frac{m^i-1}{m-1}

最少结点数

[log[n(m1)+1]]向上取整[log[n(m-1)+1]]向上取整

度=m,结点数=n,最小高度也是一样的(类似于h=n)

度=m,结点数=n,最大高度 h=n-m+1

n-m是把最后一层m个节点减掉,然后加上1就是高度

总结点数两个公式

N=N0+N1++NnN=N_0+N_1+\cdots+N_n
N=1+N1+2N2++mNmN=1+N_1+2N_2+\cdots+ mN_m

第二个公式可以理解为度数为m的节点引出m个分支,好比是天线,但是根节点没有天线所以+1

延伸

总结点数=总分支数+1总结点数=总分支数+1

总度数=总分支数

会用到的=>

二叉树

与度=2的有序树区别

  1. 度=2的树至少3个结点,但是二叉树可以为空
  2. 度=2的左右次序是相对另一个孩子而言的,如果只有一个就不用分左右次序,但是二叉树是需要的

几种特殊的二叉树

  1. 满二叉树

  2. 完全二叉树

    1. 若i≤[n/2]向下取整后,则结点 i 为分支结点,否则为叶结点
    2. 叶结点只可能在最大的两层上出现。对于下面的叶结点,都靠左
    3. 若有度为1的结点,则最多只可能有一个,且该结点只有左孩子而无右孩子。
    4. 按层序编号后,一旦出现某结点(编号为i)为叶结点或只有左孩子,则编号大于i的结点均为叶结点。(参考12号节点)
    5. 若n为奇数,则每个分支结点都有左孩子和右孩子;若n为偶数,则编号最大的分支结点(编号为n/2)只有左孩子,没有右孩子,其余分支结点左、右孩子都有。
  3. 二叉排序树

    左节点大于右节点

总结篇

二叉树的顺序存储

记得 找父节点是i/2之后向下取整

判断i是否为叶子分支节点

i>[n/2]向下取整i>[n/2]向下取整

链式存储

二叉树求空指针域

n个结点、2n个指针域,每个结点带一个天线但是root无,所以被占用的指针域有n-1个

空指针域=2n-(n-1)=n+1

三叉树空指针域or三叉链式带父节点的

需要看场合来分析,比如说三叉树的就是3n个指针域

三叉链式带父节点的那种是二叉树

错题

5.2.06

08

14完全二叉树 n求叶结点数

完全二叉树 叶结点数求结点数最大最少值

最少的话,就直接那几个叶子节点就是最后一层

最多的话,那几个叶子节点是倒数第二层的最右边

总结

爆率最高:

  1. 当前层结点数:2h12^{h-1}
  2. 总结点数:2h12^h-1
  3. 完全二叉树第i个的祖先:[i/2]向下取整
  4. 完全二叉树
    1. n=2k为偶数:n1=1,n0=k,n2=k-1
    2. n=2k-1为奇数:n1=0,n0=k,n2=k-1
  5. n0=n1+1n_0=n_1+1
  6. 完全二叉树高度常用[log2n+1log_2{n}+1]

遍历

请务必记住分支节点逐层展开法

还有从你的全世界路过法

层次遍历

  1. 根节点入队
  2. 如果队列不空,出队一个并访问该节点,将其左右孩子入队
  3. 直到队列为空

由遍历序列构造二叉树

无论哪个遍历都得加上中序遍历才能确定唯一的二叉树

前序+中序

这个方法屌啊,由前序遍历找到根节点,再区分左右

线索二叉树

注意,线索二叉树的数据结构有变

typedef struct TreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int lflag,rflag;//tag=0为指向孩子,1为线索
}

注意,算法中的

判断条件,有点离谱的

加入左节点的:p->lchild==NULL

加入右节点的,不会一开始就用p判断p->rchild==NULL,因为要将pre指向后驱节点

所以判断条件为 pre!=NULL&&pre->rchild==NULL

最后遍历完,注意处理最后一个节点的right=NULL

先序线索化可能会出现的转圈问题

在根节点加入线索后,第一个左边递归需要判断是不是左孩子线索

中序线索二叉树的遍历

找中序后继

找中序后继:

  1. 找到第一个被中序遍历的节点(就是最左下角)
    1. while(p->ltag==0)p=p->lchild;
    2. 意思是找到最后一个左下角不带线索的
  2. 找p的后继结点:
    1. 如果tag=1(不是叶子节点),右孩子就是线索,就是中序后继
    2. 如果tag=0(后继有人)要找右节点的最左下角的,中序是左根右,左边会递归 然后 再回溯到根输出根
  3. visit

找中序前驱

逐层展开法看上去真的挺好用的

先序

找先序前驱

后续后驱

总结

不用背,现推

中序先序后续
找前驱×
找后继×

​ 除非有三叉链表实现找爹

森林

存储结构

双亲表示法(顺序)

优点:找双亲节点方便

缺点,找孩子不方便,要遍历数组

孩子表示法(顺序+链式)

优点:找孩子方便

缺点:找爹不行

孩子兄弟表示法(链式)

总结

森林、树、二叉树的转换

主要为孩子兄弟表示法为核心存储

左孩子右兄弟

兄弟右指针用糖葫芦串起来

孩子左指针挂下面