数据结构学习笔记(5树)

1.定义

树是n(n大于等于0)个结点的有限集合。在任意一棵非空树中应满足:

(1)有且仅有一个特定的称为根的结点。

(2)当n大于1时,其余结点可分为m个互不相交的有限集合T1、T2、…、Tm,其中每个集合本身又是一棵树,并且称为根结点的子树

2.性质

(1)结点数=总度数+1;

(2)区分:

树的度(度为m的树):各结点的度的最大值,任意结点的度小于等于m,至少有一个结点度=m(有m个孩子),且该树至少有m+1个结点。

m叉树:每个结点最多只能有m个孩子的树,允许所有结点的度都小于m,且可以是空树。

(3)高度为h的m叉树至多有 m h − 1 m − 1 \frac{m^{h} - 1}{m - 1} m1mh1个结点。(第一层最多m的0次,第二层m的1次,第三层m的2次,进行等比数列求和)

(4)设非空二叉树中度为0(叶子结点)、1和2的结点个数分别是n0、n1、n2,则n0=n2+1.*

3.特殊的树

满二叉树与完全二叉树

满二叉树:一棵高度为h,且含有 2 h − 1 2^{h} - 1 2h1个结点的二叉树。

完全二叉树:当且仅当其每个结点都与高度为h的满二叉树中编号为1-n的结点一一对应时,称为完全二叉树。

满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树。

满二叉树 完全二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N7secfxO-1644293252580)(数据结构学习笔记.assets/image-20220206171928471.png)]

完全二叉树的特点:

(1)只有最后两层可能有叶子结点;

(2)最多只有一个度为1的结点;

(3)按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1,结点i的父节点为 ⌊ i / 2 ⌋ \left\lfloor {i/2} \right\rfloor i/2

(4)当 i < ⌊ n / 2 ⌋ i < \left\lfloor {n/2} \right\rfloor i<n/2时为分支结点,当 i > ⌊ n / 2 ⌋ i > \left\lfloor {n/2} \right\rfloor i>n/2时为叶子结点

对于完全二叉树而言,如果某结点只有一个孩子,那么一定是左孩子。

具有n个结点的完全二叉树的高度h为 ⌈ l o g 2 ( n + 1 ) ⌉ \left\lceil {{log}_{2}\left( n + 1 \right)} \right\rceil log2(n+1) ⌊ l o g 2 n ⌋ + 1 \left\lfloor {{log}_{2}n} \right\rfloor + 1 log2n+1.

**[推导]**高为h的满二叉树共有 2 h − 1 2^{h} - 1 2h1个结点,高为h-1的满二叉树共有 2 h − 1 − 1 2^{h-1} - 1 2h11个结点,则:

2 h − 1 − 1 < n ≤ 2 h − 1 2^{h - 1} - 1 < n \leq 2^{h} - 1 2h11<n2h1,则 h − 1 < l o g 2 ( n + 1 ) ≤ h h - 1 < {log}_{2}\left( {n + 1} \right) \leq h h1<log2(n+1)h,故h= ⌈ l o g 2 ( n + 1 ) ⌉ \left\lceil {{log}_{2}\left( n + 1 \right)} \right\rceil log2(n+1)

另一边,高为h的完全二叉树至少有 2 h − 1 2^{h-1} 2h1个结点,至多有 2 h − 1 2^{h} - 1 2h1个结点,故 2 h − 1 ≤ n ≤ 2 h 2^{h - 1} \leq n \leq 2^{h} 2h1n2h,则 h − 1 < l o g 2 n < h h - 1 < {log}_{2}n < h h1<log2n<h,故h= ⌊ l o g 2 n ⌋ + 1 \left\lfloor {{log}_{2}n} \right\rfloor + 1 log2n+1.

(5)(性质*的推论)对于完全二叉树,可以由结点数n推出度为0、1、2的结点个数为n0、n1、n2。

**[推导]**完全二叉树最多只有一个度为1的结点,即n1=0或1,而n0=n2+1,即n0+n2一定为奇数,则:

若完全二叉树有2k(偶数)个结点,必有n1=1,n0=k,n2=k-1;

若完全二叉树有2k-1(奇数)个结点,必有n1=0,n0=k,n2=k-1。

二叉排序树

一个二叉树或是空二叉树,是具有以下性质的二叉树:

左子树上所有结点的关键字均小于根节点的关键字;右子树上所有结点的关键字均大于根节点的关键字。

左子树和右子树又各是一棵二叉排序树。

平衡二叉树

树上任一结点的左子树右子树的深度之差不超过1。平衡二叉树是“胖胖的、丰满的树”,能有更高的搜索效率。

4.二叉树的存储结构

(1)顺序存储
#define MaxSize 100
struct TreeNode{
    ElemType value;		//结点中的数据元素
    bool isEmpty;		//结点是否为空
}

定义一个长度为MaxSize的数组t:TreeNode t[MaxSize],按照从上至下、从左至右的顺序依次存储完全二叉树中的各个结点。初始化时利用循环将所有结点标记为空:t[i].isEmpty=true;

二叉树的顺序存储中,要把二叉树的结点编号与完全二叉树对应起来。最坏情况是:高度为h且只有h个结点的单支树(所有结点只有右孩子),也至少需要 2 h − 1 2^{h} - 1 2h1个存储单元。顺序存储只适合存储完全二叉树。

(2)链式存储
typedef struct BiTNode
{
    ElemType data;						//数据域
    struct BiTNode *lchild,*rchild;		//左、右孩子指针
}BiTNode,*BiTree;
//定义一棵空树
BiTree root=NULL;
//插入根结点
root= (BiTree)malloc(sizeof(BiTNode));
root->data={1};
root->lchild=NULL;
root->rchild=NULL;
//插入新结点
BiTNode *p= (BiTNode *)malloc(sizeof(BiTNode));
p->data={2};
p->lchild=NULL;
p->rchild=NULL;
root->lchild=p;		//作为根结点的左孩子
img

由图可知,在n个结点的二叉链表中,除头结点以外,其余结点均有前驱,则有n-1个前驱,故在n个结点的二叉链表共有n+1个空链域。(空链域可用于构造线索二叉树)

若在二叉树中,要实现方便找父结点,则可定义三叉链表struct BiTNode *parent;

5.二叉树的遍历

先中后序遍历

先序遍历:左右(NLR) 中序遍历:左右(LNR) 后序遍历:左右(LRN

后序遍历(InOrder)的操作如下:

(1)若二叉树为空,则什么也不做;

(2)若二叉树非空:

【1】先序遍历左子树; 【2】先序遍历右子树; 【3】访问根结点

//后序遍历
void PostOrder(BiTree T)
{
    if(T!=NULL)
    {
        PostOrder(T->lchild);		//递归遍历左子树
        PostOrder(T->rchild);		//递归遍历右子树
        visit(T);					//访问根结点
    }
}

先序遍历实操步骤:

从根节点出发,画一条路:如果左边还有没走的路,优先往左边走;走到路的尽头(空结点)则往回走,如果左边没路了,就往右边走;如果左、右都没路了,则往上面走。先序遍历时,每个结点都会被路过三次,当第一次路过时访问结点。

image-20220206215440767
层序遍历

算法思想:

(1)初始化一个辅助队列;

(2)根结点入队;

(3)若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(若有)。

void LevelOrder(BiTree T)
{
	LinkQueue Q;
    InitQueue(Q);						//初始化辅助队列
    BiTree p;
    EnQueue(Q,T);						//将根节点入队
    while(!IsEmpty(Q))					//队列不空则循环
    {
        DeQueue(Q,p);					//队头结点出队
        visit(p);						//访问出队结点
        if(p->lchild!=NULL)
            EnQueue(Q,p->lchild);		//左孩子入队
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);		//右孩子入队
    }
}

6.线索二叉树

概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbYImIZX-1644293252581)(数据结构学习笔记.assets/image-20220207152838458.png)]

线索指向的是该序的前驱/后继。

线索二叉树找前驱/后继
中序线索二叉树

中序遍找历中序后继:

​ 左 根

​ 左 根 ( 根 右)

​ 左 根 (( 根 右) 根 右)

在中序线索二叉树中找到指定结点*p的中序后继next:

(1)若p->rtag==1,则next=p->rchild

(2)若p->rtag==0,则p必有有孩子。

//找到以p为根的子树中,第一个被中序遍历的结点
ThreadNode *Firstnode(ThreadNode *p)
{
    //循环找到最左下结点(不一定是叶结点)
    while(p->ltag==0) p=p->lchild;
    return p;
}
//在中序线索二叉树中找到结点p的后继结点
ThreadNode *NextNode(ThreadNode *p)
{
    if(p->rtag==0) return Firstnode(p->rchild);
    else return p->rchild;		//rtag==1直接返回后继线索
}
//对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
void Inorder(ThreadNode *T)
{
    for(ThreadNode *p=FirstNode(T);p!=NULL;p=NextNode(p))
    	visit(p);
}
先序线索二叉树

先序遍历找先序后继:(假设有左孩子)

​ 根

​ 根 ( 左 右) 右

[结论]若p有左孩子,则先序后继为左孩子。

(假设没有左孩子)

​ 根

​ 根 ( 左 右)

[结论]若p有右孩子,则先序后继为右孩子。

在中序线索二叉树中找到指定结点*p的先序后继next:

(1)若p->rtag==1,则next=p->rchild

(2)若p->rtag==0,则p必有右孩子。

(仅改用三叉链表或从根开始遍历寻找才能完成)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YEDrruad-1644293252582)(数据结构学习笔记.assets/image-20220207163326461.png)]

后序线索二叉树

后序遍历找后序前驱:(假设有右孩子)

​ 左

​ 左 (左 根) 根

[结论]若p有右孩子,则后序前驱为左孩子。

(假设没有右孩子)

​ 左

​ (左 根) 根

[结论]若p没有右孩子,则后序前驱为左孩子。

在中序线索二叉树中找到指定结点*p的后续前驱next:

(1)若p->ltag==1,则next=p->lchild

(2)若p->ltag==0,则p必有左孩子。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bbS94EFb-1644293252583)(数据结构学习笔记.assets/image-20220207164018118.png)]

7.树的存储结构

(1)双亲表示法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IeI4n96O-1644293252584)(数据结构学习笔记.assets/image-20220207165231551.png)]

(2)孩子表示法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6uVa92rE-1644293252585)(数据结构学习笔记.assets/image-20220207165311301.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ylDU533f-1644293252586)(数据结构学习笔记.assets/image-20220207165325823.png)]

(3)孩子兄弟表示法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wA1PR5io-1644293252587)(数据结构学习笔记.assets/image-20220207165704155.png)]

类似地,可以实现森林和二叉树的转换。

typedef struct CSNode
{
    ElemType data;
    struct CSNode *firstchild,*nextsibling;			//看作左指针与右指针,代表的是第一个孩子和右兄弟指针
}CSNode,*CSTree;

8.二叉排序树

二叉排序树又称二叉查找树(BST,Binary Search Tree),在该树中,左子树结点值<根结点值<右子树结点值。对该数进行中序遍历,可以得到一个递增的有序序列。

查找
算法
image-20220207172849063
查找效率分析

查找长度:在查找运算中,需要对比关键字的次数称为查找长度,反映了查找操作时间复杂度。查找成功的平均查找长度称ASL(Average Search Length)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BCZSFekq-1644293252588)(数据结构学习笔记.assets/image-20220207225108830.png)]

若树高为h,找到最下层的一个结点需要对比h次。最好情况:n个结点的二叉树最小高度为 ⌊ l o g 2 n ⌋ + 1 \left\lfloor {{log}_{2}n} \right\rfloor + 1 log2n+1,平均查找长度= O( l o g 2 n {log}_{2}n log2n);

最坏情况:每个结点只有一个分支,树高h=结点数n。平均查找长度=O(n)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LRrZUaRw-1644293252589)(数据结构学习笔记.assets/image-20220207225428931.png)]

插入

若原二叉排序树为空,则直接插入结点;否则,若关键字k小于根结点值,则插入到左子树,若关键字k大于根结点值,则插入到右子树。由于新插入的结点一定是叶子,且该算法是递归实现的,故最坏的空间复杂度为O(h)(h为树的高度)。

//在二叉排序树插入关键字为k的新结点(递归实现)
int BST_insert(BSTree &T,int k)
{
    if(T==NULL)
    {
        T=(BSTree)malloc(sizeof(BSTNode));
        T->key=k;
        T->lchild=T->rchild=NULL;
        return 1;			//返回1,插入成功
    }
    else if(k==T->key)		//树中存在相同关键字的结点,插入失败
        return 0;
    else if(k<T->key)		//插入到T的左子树
        return BST_Insert(T->lchild,k);
    else					//插入到T的左子树
        return BST_Insert(T->rchild,k);
}
删除

先搜索找到目标结点:

(1)若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质(即左子树结点值<根结点值<右子树节点值);

(2)若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置;

(3)若结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删去原来位置上的直接后继(或直接前驱),这样就转换成了第一或第二种情况。

[]进行中序遍历,可以得到一个递增的有序序列,故z的后继即为z的右子树中最左下结点(该结点一定没有左子树,然后可以参考第二种情况)。

9.平衡二叉树

定义

平衡二叉树(Balanced Binary Tree),简称平衡树(AVL树),其树上任一结点的左子树和右子树的高度之差不超过1。

结点的平衡因子=左子树高-右子树高。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oiOT5X3Q-1644293252591)(数据结构学习笔记.assets/image-20220207225710023.png)]

在插入操作中,每次调整的对象都是“最小不平衡子树”,只要将最小不平衡子树调整平衡,则其他祖先结点都会恢复平衡。

调整最小不平衡子树A

目标:【1】恢复平衡;【2】保持二叉排序树的特性(即左子树结点值<根结点值<右子树结点值)。

只有左孩子才能右旋,只有有孩子才能左旋。

LL插入

在A的左孩子的左子树中插入导致不平衡

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yid56aw0-1644293252592)(数据结构学习笔记.assets/image-20220207230211251.png)]由于在结点A的左孩子(L)的左子树(L)上插入了新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。将A的左孩子B向右上旋转代替A成为根结点,将A结点向右下旋转成为B的右子树的根结点,而B的原右子树则作为A结点的左子树。

RR插入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3b6n8fUI-1644293252593)(数据结构学习笔记.assets/image-20220207230528747.png)]

由于在结点A的右孩子(R)的右子树(R)上插入了新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。将A的右孩子B向左上旋转代替A成为根结点,将A结点向左下旋转成为B的左子树的根结点,而B的原左子树则作为A结点的右子树。

代码思路:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LLInONd1-1644293252594)(数据结构学习笔记.assets/image-20220207231352617.png)]

LR插入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zaVH4hCf-1644293252595)(数据结构学习笔记.assets/image-20220207231636433.png)]

由于在A的左孩子(L)的右子树(R)上插入新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋转后右旋转。先将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置,然后再把该C结点向右上旋转提升到A结点的位置。

RL插入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lPMWfyyH-1644293252596)(数据结构学习笔记.assets/image-20220207231813793.png)]

查找效率分析

在平衡二叉树中,树上任一结点的左子树和右子树的高度之差不超过1,假设以 n h n_{h} nh表示深度为h的平衡树中含有的最少结点数,则有n0=0,n1=1,n2=2,且有 n h = n h − 1 + n h − 2 + 1 n_{h} = n_{h - 1} + n_{h - 2} + 1 nh=nh1+nh2+1

含有n个结点的平衡二叉树的最大深度为 O ( l o g 2 n ) O\left( {log}_{2}n \right) O(log2n),即平衡二叉树的平均查找长度 O ( l o g 2 n ) O\left( {log}_{2}n \right) O(log2n)

10.哈夫曼树

一些定义

结点的权:有某些现实含义的数值;

结点的带权路径长度(WPL):从树的根到该结点的路径长度(经过的边数)与该结点上权值的乘积。

在含有n个带权叶结点的二叉树中,带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DmtONLJm-1644293252598)(数据结构学习笔记.assets/image-20220208114559064.png)]

哈夫曼树的构造及其特点

给定n个权值分别为w1, w2,…, wn的结点,构造哈夫曼树的算法描述如下:
(1)将这n个结点分别作为n棵仅含一个结点的二叉树,构成森林F;
(2)构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和;
(3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中;
(4)重复步骤2)和3),直至F中只剩下一棵树为止。

特点:

【1】每个初始结点最终都成为叶结点,且权值越小的结点到根结点的路径长度越大;
【2】哈夫曼树的结点总数为2n − 1;
【3】哈夫曼树中不存在度为1的结点;
【4】哈夫曼树并不唯一,但WPL必然相同且为最优。

哈夫曼编码

可变长度编码:允许对不同字符用不等长的二进制位表示。

若没有一个编码是另一个编码的前缀,则称这样的编码为前缀编码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcC96bfo-1644293252599)(数据结构学习笔记.assets/image-20220208120601782.png)]

值<根结点值<右子树结点值)。

只有左孩子才能右旋,只有有孩子才能左旋。

LL插入

在A的左孩子的左子树中插入导致不平衡

[外链图片转存中…(img-yid56aw0-1644293252592)]由于在结点A的左孩子(L)的左子树(L)上插入了新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。将A的左孩子B向右上旋转代替A成为根结点,将A结点向右下旋转成为B的右子树的根结点,而B的原右子树则作为A结点的左子树。

RR插入

[外链图片转存中…(img-3b6n8fUI-1644293252593)]

由于在结点A的右孩子(R)的右子树(R)上插入了新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。将A的右孩子B向左上旋转代替A成为根结点,将A结点向左下旋转成为B的左子树的根结点,而B的原左子树则作为A结点的右子树。

代码思路:

[外链图片转存中…(img-LLInONd1-1644293252594)]

LR插入

[外链图片转存中…(img-zaVH4hCf-1644293252595)]

由于在A的左孩子(L)的右子树(R)上插入新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋转后右旋转。先将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置,然后再把该C结点向右上旋转提升到A结点的位置。

RL插入

[外链图片转存中…(img-lPMWfyyH-1644293252596)]

查找效率分析

在平衡二叉树中,树上任一结点的左子树和右子树的高度之差不超过1,假设以 n h n_{h} nh表示深度为h的平衡树中含有的最少结点数,则有n0=0,n1=1,n2=2,且有 n h = n h − 1 + n h − 2 + 1 n_{h} = n_{h - 1} + n_{h - 2} + 1 nh=nh1+nh2+1

含有n个结点的平衡二叉树的最大深度为 O ( l o g 2 n ) O\left( {log}_{2}n \right) O(log2n),即平衡二叉树的平均查找长度 O ( l o g 2 n ) O\left( {log}_{2}n \right) O(log2n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值