408数据结构学习笔记——树、二叉树

简介: 408数据结构学习笔记——树、二叉树

1.树的基本概念f2f3599064bc47f9af124672b64f65ee.png

  1. 根节点:有且仅有一个
  2. 分支节点:有后继的节点
  3. 叶子节点:没有后继的节点
  4. 除根节点外,所有节点有且仅有一个前驱(与图相对)
  5. 子树:除根节点外,可以分为m个互不相交的集合(例:BEFKL、CG、DHIJM)
  6. 祖先节点:根A到结点K的从上往下的唯一路径上的任意结点,例:B是K的祖先结点
  7. 子孙结点:根A到结点K的从上往下的唯一路径上的任意结点,例:K是B的子孙结点
  8. 双亲结点(父结点):某结点的前驱。例:E是K的双亲结点,B是F的双亲结点
  9. 孩子结点:某结点的后继。例:K是E的孩子结点,F是B的孩子结点
  10. 兄弟结点:同一层上的结点。例如:E是F的兄弟结点,K是L的兄弟结点
  11. 路径:两个结点之间所经过的结点序列(单向,且由上往下,B到L有路径,而B到D没路径
  12. 路径长度:路径上所经过的边的个数
  13. 结点的层次:由上往下,A是1层,B是2层,E是3层,K是4层
  14. 结点的高度:由下往上,K是1层,E是2层,B是3层,A是4层。特别地,F也是2层
  15. 树的高度:根节点的高度,即总共多少层
  16. 结点的度:有几个孩子结点。E的度为2,K的度为0,A的度为3。特别地,度为0时,结点为叶子结点;度>0时,结点为分支结点
  17. 树的度:各个结点中度数最大的值
  18. 森林:n棵互不相交的树的集合,森林和树可以相互转换(添加根节点)

2.树的性质

  1. 结点数 = 总度数 + 1(除根节点外,每个结点都有一条边指向其前驱)
  2. 度为m的树和m叉树
  1. 相同点:允许结点的度 ≤ m

         b.不同点:m叉树的结点度数可以都<m,但是度为m的树至少有一个节点的度 = m;m叉树可以为空树,度为m的树至少有m + 1个结点(根节点 + m个叶子节点)

3.度为m的树第 i 层至多有gif.gif个结点(最多情况下:第一层为根节点,结点数1,即gif.gif;第二层为m个结点,即gif.gif第二层为gif.gif以此类推)

4.高度为h的m叉树至多又gif.gif个结点(等比数列求和)

5.高度为h的m叉树最少有h个结点;高度为h的度为m的树最少有 h + m - 1 个结点(设最后一个结点为 m,高度为 h,m + h - 1,- 1 为减去最后一个结点的高度)

3.二叉树

3.1.二叉树

3.1.1.二叉树的基本概念

  1. 二叉树由根节点和左右子树构成
  2. 每个结点至多有两个子树(注意区别度为2的树)
  3. 有序树(左右子树不能颠倒)
  4. 可以是个空树

3.1.2.二叉树的性质

设二叉树中度为0、度为1、度为2的结点个数分别为a、b、c,则a = c + 1

  1. 设 n 为结点总个数
  2. n = 0 * a + 1 * b + 2 * c + 1(结点个数 = 总度数 + 1)
  3. n = a + b + c(结点个数相加得到总个数)
  4. b + 2c + 1 = a + b + c → a = c + 1

3.2.满二叉树

  1. 除了叶结点外,所有结点都有左右子树


2.设高度为 h,共gif.gif 个结点

3.按层序编号,根节点为1,则结点 i 的左孩子为2i,右孩子为2i + 1,父结点为⌊ i / 2⌋


7d961a8e04124859b04871e85d3cc955.png

3.3.完全二叉树

3.3.1.完全二叉树的概念

  1. 当且仅当其每个结点都与高度为 h 的二叉树中编号为1 - n 的节点一一对应
  2. 只有可能有一个度为1的叶子结点(在倒数第二层)
  3. 叶子结点出现在最后两层
  4. 按层序编号,根节点为1,则结点 i 的左孩子为2i,右孩子为2i + 1,父结点为⌊ i / 2⌋
  5. i ≤ ⌊ n / 2⌋为分支节点 i > ⌊ n / 2 ⌋为叶子结点

dd5b63e7ee0b46268128173c3e9c2c7b.png

3.3.2.完全二叉树的性质

  1. 具有 n 个结点的完全二叉树的高度 h 为⌈gif.gif⌉ 或 gif.gif⌋ + 1

    1. 第 h - 1 层的完全二叉树的结点个数为gif.gif

   2. 第 h 层的完全二叉树的结点个数为gif.gif

   3.gif.gif

    4.gif.gif

2.设度为0的结点个数为a,度为1的结点个数为b,度为2的结点个数为c,完全二叉树可以推出a、b和c的值

   1. 在二叉树中,a = c + 1(二叉树的性质),因此,a + c必为奇数

    2.完全二叉树中度为1的结点个数最大为1(即b = 1 或 0)

    3.设总结点个数为2k(偶数):a + c为奇数→b = 1→a = k、c = k - 1

    4.设总结点个数为2k - 1(奇数):a + c为奇数→b=0→a = k,c = k - 1

3.5.二叉排序树

  1. 左子树的所有结点的关键字都小于根节点
  2. 右子树的所有结点的关键字都大于根节点
  3. 左右子树依然是二叉排序树

b576ce8f37b24066af4bd9235bc3fe9a.png

3.6.平衡二叉树

  1. 树上任意一个结点的左右子树的深度差不超过1
  2. 平衡二叉树具有更高的搜索效率

8e18ed6b42d4438cb20f17a58c8da237.png

4.二叉树的存储结构

4.1.二叉树的顺序存储

#define MAXSIZE 100
typedef struct treeNode{
    elemType value;    //结点中元素的值
    bool isEmpty;    //结点是否为空
}treeNode;
//申明一个长度为MAXSIZE的结构体数组
//由上至下、从左至右的方式存放完全二叉树的各个结点数据
treeNode T[MAXSIZE];
//初始化时,把结点置空
for(int i = 0; i < MAXSIZE; i++) T[i].isEmpty = true;
  1. 为了方便存储,从数组下标1开始存储数据。
  2. 树的顺序存储通常只用于存储完全二叉树(普通二叉树采用顺序存储的方式跟完全二叉树一样,但是采用这种方式的代价是浪费大量存储空间)

4.2.二叉树的链式存储

typedef struct BiTNode{
    elemtype value;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
//定义一个空树
BiTree root = NULL;
//插入根结点
root = (BiTNode*)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;    //作为根结点的左孩子
  1. n个节点的二叉链表共有n + 1个空指针域
  1. 总共n个节点,除了头结点,每个节点都被一个指针指着
  2. 2n个指针就有n-1个指针不是空指针
  3. 2n-(n-1)= n + 1
  1. 普通二叉树的链式存储中,找其双亲结点只能通过从根节点重新遍历树寻找,如果经常需要找到某个结点的父亲节点,可以增加一个指针指向双亲结点(三叉链表)
typedef struct BiTNode{
    struct BiTNode *parent, *lchild, *rchild;
    elemType value;
}BiTNode, *BiTree;

5.王道课后题

68dd84c307944dffa703a8df1c4e67bc.png

  1. 设度为0的结点个数为a,度为1的结点个数为b,度为c的结点个数为c
  2. 当b = 1时:

      1.a = c + 1,总结点个数为 0 * a + 1 * b + 2 * c + 1 = 2a

      2.高度为gif.gif向上取整

3.当b = 0时:

  1. a = c + 1,总结点个数为 0 * a + 1 * b + 2 * c + 1 = 2a - 1
  2. 高度为gif.gif向上取整

9878fbde9a3249a7af4d196d1a2dada7.png

  1. 设度为0的结点个数为a,度为1的结点个数为b,度为c的结点个数为c,总结点个数为n
  2. 度为0的结点,即叶子结点
  3. 度为2的结点,即分支节点
  4. a = c + 1 → c = a - 1
  5. 满二叉树 → b = 0
  6. n = a + b + c → n = 2a - 1 → a = (n + 1) / 2 即 叶子结点 = (n + 1) / 2
  7. n = a + b + c → n = 2c + 1 → c = (n - 1) / 2 即 分支节点 = (n - 1) / 2
  8. 高度gif.gif向上取整

3edb93a1655f430882c0d1f39f4b50ad.png

  1. 整个完全二叉树295个结点
  1. 前八层的总数为255
  2. 第九层的结点个数240
  1. 叶子结点的个数为248个
  1. 第九层为完全二叉树的最后一层,其结点都为叶子结点,即240个
  2. 第八层的结点共128个,其分支节点的个数为第九层的一半,即120个,剩下的都是叶子结点,即8个

db1779c5c8be476d99f4584b84909b09.png

#define MAXSIZE 100
typedef struct treeNode{
    elemType value;
    bool isEmpty;
}treeNode;
treeNode T[MAXSIZE];
elemType commonParent(treeNode T, int p, int q){
    //寻找公共结点
    while (p != q){
        //谁大谁找双亲结点
        if (p > q) p /= 2;
        else q /= 2;
    }
    return T[p].value;
}


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

热门文章

最新文章