数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构(上)

简介: 数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构

1.树概念及结构

1.1树的概念

树是一种非线性的数据结构,它是由 n(n >= 0)个有限节点组成的一个具有层次关系的集合。

那么为什么叫 "树" 呢? (节点也可以称结点,建议称结点,和结构体对上)

之所以把它成为 "树",是因为它很像现实生活中的树。只是它是倒过来的,根朝上叶子朝下。

① 树有一个特殊的结点,成为根结点,根节点不存在前驱结点。

② 除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、……、Tm,

其中每一个集合 Ti(1 <= i <= m) 又是一颗结构于树类似的字数。

每颗子树的结点有且只有一个前驱,可以有0个或多个后继。

③ 因此,树是递归定义的。因为任何树都会被分成根和子树。


1.2树的相关概念

节点的度一个节点含有的子树的个数称为该节点的度; 如上图:A的为6

叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点

非终端节点或分支节点度不为0的节点; 如上图:D、E、F、G...等节点为分支节点

双亲节点或父节点若一个节点含有子节点,则这个节点称为其子节点的父节点;

如上图:A是B的父节点

孩子节点或子节点一个节点含有的子树的根节点称为该节点的子节点

如上图:B是A的孩子节点

兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点

树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

( 有些书上规定根节点的层数为0,这样空树就是-1了,所以若没有特殊说明默认规定根节点的层数为 1)

树的高度或深度:树中节点的最大层次; 如上图:树的高度为4

节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙

森林:由m(m>0)棵互不相交的多颗树的集合称为森林;(数据结构中的学习并查集本质就是

一个森林)

注意:树型结构中,子树之间不能有交集,否则就不是树形结构。


1.3树的表示

以前学单链表时只有一个指针,双链表两个指针,但是树有多少个指针是不确定的,

因为树没有规定一个节点最多有多少个孩子。那该如何定义结构呢?


法一:假设说明了树的度为N,才能勉强用

 
struct TreeNode 
{
    int data;
    struct TreeNode* sub[N]; // 指针数组
};

问题点:

① 可能会存在不少的空间浪费。

② 万一没有限定树的度为多少呢?


法二:vector顺序表(C++中这里可以用 vector,但是C里没有)

 
// 假设我们定义了一个顺序表
typedef int STLDataType;  //顺序表的数据类型
typedef struct TreeNode* SLDataType; // 顺序表中存节点的指针
struct TreeNode //SeqList
{
    STLDataType data;
    SeqList s;  // s为SLDataType* array;
};

即使你没有告诉我度是多少,我有多少个孩子我就存多少个孩子,所以这里不需要关心度的问题。

但是这里 s 的结构相对复杂,s 里面有一个类型为SLDataType* 的数组,

这个数组已经是二级指针了,SLDataType 展开后又是一个 struct TreeNode* 。


法三:双亲表示法

 
struct TreeNode 
{
    int parenti;
    int data;
};

[ A -1] [ B0 ] [ C0 ] [ D0 ] ...... [ H 3 ]

每一个元素中存的是结构体 struct TreeNode arr[10]

每个元素内只存自己的值和父亲的下标

(A没有父亲是-1,B的父亲下标是0…… H的父亲是D下标为3),可以通过一个值找到自己父亲。


上列的方式各有优缺点,那么有没有最优的方法?

当然有,它就是左孩子右兄弟表示法

 
typedef int DataType;
struct Node 
{
    struct Node* leftChind1;   // 永远指向第一个孩子
    struct Node* rightBrother;  // 指向孩子右边的兄弟
    DataType _data;
};

5cb51039f77d4bc18cb0c7f7268ca5ca.png

解读:无论你有多少个孩子,它都只存两个指针。一个指针永远指向第一个孩子,

另一个指针指向孩子右边的兄弟(亲兄弟)。这个树的度无论为多少,也不需要用顺序表存,

但是你任何一个节点有多少个孩子都能给你表示出来,通过第一个孩子把所有孩子都找出来

不复杂也没有浪费,只用两个指针就把链接关系都表示出来了,这就是巨佬的智慧。


1.4树在实际中的运用

文件系统的目录树结构、网络拓扑,最短路径问题,搜索引擎、思维导图等

2.二叉树概念及结构

2.1二叉树的概念

定义:二叉树既然叫二叉树,顾名思义即度最大为2的树称为二叉树。

它的度可以为 1 也可以为 0,但是度最大为 2 。

一颗二叉树是节点的一个有限集合,该集合:

① 由一个根节点加上两颗被称为左子树和右子树的二叉树组成

② 或者为空

观察上图我们可以得出如下结论:

① 二叉树不存在度大于 2 的节点,换言之二叉树最多也只能有两个孩子。

② 二叉树的子树有左右之分,分别为左孩子和右孩子。次序不可颠倒,因此二叉树是有序树。

注意:对于任意的二叉树都是由以下几种情况复合而成的:


2.2 满二叉树


定义:一个二叉树,如果每一层的节点数都达到了最大值(均为2),


则这个二叉树就可以被称作为 "满二叉树" 。


换言之,如果一个二叉树的层数为h ,且节点总数是 2^h-1,则他就是一个满二叉树。


层数为N的计算公式:

4749d7f5def9a81648dd2f63321b99f3.gif

5b5a9e737e489b868a99fbb4e8aa3165.gif

数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构(下):https://developer.aliyun.com/article/1513413

目录
相关文章
|
8月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
256 10
 算法系列之数据结构-二叉树
|
8月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
206 3
 算法系列之数据结构-Huffman树
|
8月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
635 19
|
10月前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
328 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
10月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
188 10
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
999 9
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
270 59
|
5月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
105 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
10月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
470 77

热门文章

最新文章