数据结构与算法——树与二叉树_数据结构与算法树和二叉树

i

i 层上之多有 2i-1 个结点(

i

=

1

i>=1

i>=1)

🚗性质二: 深度为

k

k

k 的二叉树之多有 2k-1 个结点(

k

=

1

k>=1

k>=1)

🚗性质三: 对任何一棵二叉树T,如果其终端结点数为

n

0

n0

n0,度为2的结点数为

n

2

n2

n2,则有

n

0

=

n

2

1

n0=n2+1

n0=n2+1

在这里插入图片描述

🚢满二叉树

一棵深度为

k

k

k 且有 2k-1 个结点的二叉树称为👉满二叉树👈,这种树的✍特点是每一层上的结点数都是最大结点数,只有最后一层是叶子结点且不存在度为1的结点

在这里插入图片描述

🚢完全二叉树

深度为

k

k

k 的,有

n

n

n 个结点的二叉树,当且仅当其每一个结点都与深度为

k

k

k 的满二叉树中编号从1至

n

n

n 的结点一一对应时,称之为👉完全二叉树👈✍在一棵完全二叉树中只有最后两层可能为叶子结点,且最多只有一个度为1的结点。

在这里插入图片描述

✨完全二叉树的两个重要特性:

🚗特性一: 具有

n

n

n个结点的完全二叉树的深度为

l

o

g

2

n

log2n

log2n向下取整+1

🚗特性二: 如果对一棵有

n

n

n个结点的完全二叉树(其深度为

l

o

g

2

n

log2n

log2n向下取整+1)的结点按层序编号(从第一层到第

l

o

g

2

n

log2n

log2n向下取整+1层,每层从左到右),则对任一结点

i

1

<

=

i

<

=

n

i(1<=i<=n)

i(1<=i<=n),有以下结论:

如果

i

=

1

i=1

i=1,则结点

i

i

i是二叉树的根,无双亲,如果

i

1

i>1

i>1,则双亲(Parent)为结点

i

/

2

i/2

i/2向下取整;

如果

2

i

n

2i>n

2i>n,则结点i无左孩子(结点

i

i

i为叶子结点);否则其左孩子(Lchild)是结点2i;

如果

2

i

1

n

2i+1>n

2i+1>n,则结点i无右孩子,否则其右孩子(Rchild)是结点

2

i

1

2i+1

2i+1;

在这里插入图片描述

🚗特性三: 满二叉树是一种特殊的完全二叉树,但完全二叉树不一定是满二叉树

🚢二叉排序树

左子树上所有结点的关键字均小于根结点的关键字,右子树上所有结点的关键字均大于根结点的关键字,左子树和右子树又各是一棵二叉排序树。

✨二叉排序树用于元素的搜索和排序。
在这里插入图片描述

🚢平衡二叉树

树上任何一个结点的左子树和右子树的深度之差不超过1。

✨平衡二叉树能有更高的搜索效率。

在这里插入图片描述

🚢二叉树的存储结构
🌈顺序存储结构

按照顺序存储结构的定义,用一组地址连续的存储单元依次✍自上而下、从左往右存储完全二叉树的结点元素,即将完全二叉树上编号为

i

i

i的结点元素存储在如上定义的一维数组中下标为

i

1

i-1

i−1的分量中。

//二叉树的顺序存储表示
#define MaxSize 100 //二叉树的最大结点数
struct TreeNode
{
   ElemType value;      //结点中的数据元素
   bool isEmpty;        //结点是否为空
};

//定义一个长度为MaxSize的数组t,按照从上而下,从左至右的顺序依次存储二叉树中的各个结点
TreeNode t[MaxSize];


对于一般二叉树,则应将其每个结点与完全二叉树上的结点相对照,存储在一维数组中的对应分量中。✍顺序存储结构更多仅适用于完全二叉树的存储。

🌈链式存储结构

由二叉树的定义得知,二叉树的结点由一个数据元素和分别指向其左、右子树的两个分支构成,则表示二叉树的链表中的结点至少包含三个域:👉数据域👈👉左、右指针域👈。有时,为了便于找到结点的双亲,则还可以在结点结构中增加一个指向其双亲结点的指针域。 利用这两种结点结构所得的二叉树的存储结构分别称之为👉二叉链表👈👉三叉链表👈。

//二叉树的二叉链表存储表示
typedef struct BiTNode
{
    TElemType data;		//数据域
    struct BiTNode \*lchild, \*rchild      //左右孩子指针
}BiTNode,\*BiTree;


🚢二叉树的遍历

在二叉树的一些应用中,常常要求在树中查找具有某种特征的结点,或者对树中全部结点逐一进行某种处理。回顾二叉树的递归定义可知,二叉树是由三个基本单元组成:根节点、左子树和右子树,因此,✍二叉树的遍历分为先(根)序遍历、中(根)序遍历、后(根)序遍历和层次遍历。

🚩先(根)序遍历二叉树的操作定义为(根左右):
若二叉树为空,则空操作,否则:

  • 访问根结点;
  • 先序遍历左子树;
  • 先序遍历右子树;
    在这里插入图片描述

🚩中(根)序遍历二叉树的操作定义为(左根右):
若二叉树为空,则空操作,否则:

  • 中序遍历左子树;
  • 访问根结点;
  • 中序遍历右子树;
    在这里插入图片描述

🚩后(根)序遍历二叉树的操作定义为(左右根):
若二叉树为空,则空操作,否则:

  • 后序遍历左子树;
  • 后序遍历右子树;
  • 访问根结点;
    在这里插入图片描述

🚩层次遍历二叉树的操作定义为:

  • 初始化一个队列;
  • 根结点入队;
  • 若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾;
  • 重复上一步,直到队列为空
    在这里插入图片描述
🚢线索二叉树

在遍历二叉树时,要想找到某个结点的前驱和后继的信息,得运用二叉树已知的遍历方法从头开始遍历,然后在遍历的过程中动态获取其结点的前驱和后继的信息,那有没有一种方法是已知该结点就已经知道其前驱和后继的信息了呢?答案肯定是有的,为了解决二叉树遍历时所存在的一些问题,线索二叉树也孕育而生。那什么是👉线索二叉树👈呢?

简单理解就是✍利用空链域来存放结点的前驱和后继的信息,方便寻找前驱结点和后继结点,提高遍历效率。因为n个结点的二叉树,有

n

1

n+1

n+1个空链域,所以可以利用这些空链域来记录前驱,后继的信息。**若结点有左子树,则其

l

c

h

i

l

d

lchild

lchild域指示其左孩子,否则令

l

c

h

i

l

d

lchild

lchild域指示其前驱;若结点有右子树,则其

r

c

h

i

l

d

rchild

rchild域指示其右孩子,否则令

r

c

h

i

l

d

rchild

rchild域指示其后继。为了避免混淆,尚需改变结点结构,增加两个标志域**
在这里插入图片描述

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

📌在线索树上进行遍历,只要先找到序列中的第一个结点,然后依次找结点后继直至其后继为空时而止。


🚀树和森林

🚢树的存储结构

双亲表示法

每个结点中保存指向 “双亲” 的指针。

优点:查找指定结点的双亲很方便

缺点:查找指定结点的孩子只能从头遍历

//树的双亲表示法存储表示
#define MAX\_TREE\_SIZE 100
typedef struct PTNode{      //结点结构
    TElemType data;
    int parent;     //双亲位置域
}PTNode;
typedef struct{     //树结构
    PTNode nodes[MAX_TREE_SIZE];
    int r,n;    //根的位置和结点数
}PTree;

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

//树的孩子表示法链表存储表示
typedef struct CTNode{      //孩子结点
    int child;
    struct CTNode \*next;
}\*ChildPtr;
typedef struct{
    TElemType data;
    ChildPtr firstchild;    //孩子链表头指针
}CTBox;
typedef struct{
    CTBox nodes[MAX_TREE_SIZE];
    int n,r;        //结点数和根的位置
}CTree;

子兄弟表示法(链式存储)

优点,可以利用二叉树操作来处理树

//树的孩子兄弟表示法链表存储表示
typedef struct CSNode{      //孩子结点
    ElemType data;
    struct CSNode \*firstchild, \*nextsibling;
}CSNode,\*CSTree;


🚢树和森林的遍历
🌈树的遍历

树的先根遍历:若树非空、先访问根结点,再依次对每棵子树进行先根遍历

树的后根遍历:若树非空、先依次对每棵子树进行后根遍历,最后再访问根结点

树的层次遍历(用队列实现)

  • 若树非空,则根结点入队
  • 若队列非空,队头元素出队并访问,同时将该元素的孩子依次入队
  • 重复2直到队列为空
🌈森林的遍历

先序遍历森林

若森林非空,则可按照下述规则遍历:

  • 访问森林中第一棵树的根结点;
  • 先序遍历第一棵树中根结点的子树森林;
  • 先序遍历除去第一棵树之后剩余的树构成的森林

中序遍历森林

若森林非空,则可按照下述规则遍历:

  • 中序遍历森林中第一颗树的根结点的子树森林;
  • 访问第一棵树的根结点;
  • 中序遍历除去第一棵树的根结点的子树森林

🚀哈夫曼树

👉哈夫曼(Huffman)树👈,又称为最优树✍是一类带权路径长度最短的树

🚢最优二叉树(哈夫曼树)

假设有

n

n

n个权值

(

W

1

W

2

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

🌈树的遍历

树的先根遍历:若树非空、先访问根结点,再依次对每棵子树进行先根遍历

树的后根遍历:若树非空、先依次对每棵子树进行后根遍历,最后再访问根结点

树的层次遍历(用队列实现)

  • 若树非空,则根结点入队
  • 若队列非空,队头元素出队并访问,同时将该元素的孩子依次入队
  • 重复2直到队列为空
🌈森林的遍历

先序遍历森林

若森林非空,则可按照下述规则遍历:

  • 访问森林中第一棵树的根结点;
  • 先序遍历第一棵树中根结点的子树森林;
  • 先序遍历除去第一棵树之后剩余的树构成的森林

中序遍历森林

若森林非空,则可按照下述规则遍历:

  • 中序遍历森林中第一颗树的根结点的子树森林;
  • 访问第一棵树的根结点;
  • 中序遍历除去第一棵树的根结点的子树森林

🚀哈夫曼树

👉哈夫曼(Huffman)树👈,又称为最优树✍是一类带权路径长度最短的树

🚢最优二叉树(哈夫曼树)

假设有

n

n

n个权值

(

W

1

W

2

[外链图片转存中…(img-lrPUFP21-1714519916857)]
[外链图片转存中…(img-xLtyXzsk-1714519916858)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值