数据结构中篇 - 树与图

简介: 本篇文章属于数据结构连载篇的其中之一,共分为上中下三篇。上篇主要介绍线性数据结构数组、链表、栈、队列;中篇介绍树和图;下篇介绍复合数据结构哈希表、跳表等。希望大家持续关注,谢谢。

1 树

1.1 概念

image.png

节点

  • 节点 :每一个都称为节点
  • 根节点:根上的节点(第一个开始节点)
  • 叶子节点:没有分支的都称为叶子节点

树的高度、深度、层

  • 高度 由下至上 0,1,2...
  • 深度 由上至下 0,1,2...
  • 层 由上至下 1,2,3...

特点

  • 每个节点有零个或多个子节点
  • 没有父节点的节点称为根节点
  • 每一个非根节点有且只有一个父节点
  • 除了根节点外,每个子节点可以分为多个不相交的子树

1.2 二叉树

1.2.1 概念

二叉树的任何一个节点的子节点数量不超过2,子节点分左节点和右节点 不能随意颠倒。

  • 普通二叉树:每个节点最多有两个孩子节点
  • 满二叉树:除了叶子节点每个节点都有左右两个孩子节点,所有叶子节点在同一层
  • 完全二叉树:从树的根节点,从上到下,从左到右一次添满节点形成的二叉树是完全二叉树

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

    image.png

二叉树的遍历:

  • 前序遍历:根节点->左子树->右子树
  • 中序遍历:左子树->根节点->右子树
  • 后序遍历:左子树->右子树->根节点

1.2.2 二叉堆

什么是堆呢?它是满足以下要求的二叉树

  • 完全二叉树
  • 每个节点>= or <= 孩子节点(大顶堆,小顶堆)

    我们常说的堆排序就是借助堆顶元素的特点,衍生出来的一种排序算法

访问的时间复杂度(×)

搜索的时间复杂度(O(1)只关注堆顶元素)

插入的时间复杂度(O(logn))

删除的时间复杂度(O(logn)删除一般也是删除堆顶元素)

堆化操作(将一组树转化为一个堆)时间复杂度为(O(N))

Java中可以使用PriorityQueue<>创建堆。

1.2.3 二叉查找树

二叉查找树,也称为二叉搜索树、有序二叉树或二叉排序树,就是让二叉树的数据结构具备二分查找的特点。

特点:

  • 任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  • 任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  • 任意节点的左、右子树也分别为二叉查找树;
  • 没有键值相等的节点。

查找、插入的时间复杂度较低为 O ( log ⁡ n )

1.2.4 平衡二叉树(AVL树)

平衡二叉树是为了优化二叉查找树,如果二叉查找树的两个子树数据相差过大,就不能很好的实现二分查找,甚至随着数据的不断加入,二叉查找树会退化变成一个单链表,或者近似的单链表。

平衡二叉树借助自旋的方式,当插入数据使两棵子树的高度差大于1,就会触发自旋

特点:

  • 任何节点的两棵子树的高度差不大于1的二叉树。

1.2.5 红黑树

红黑树相比大家都如雷贯耳,每当我们提及Java中的HashMap都会说它的底层实现是数组+链表+红黑树,这就是一种典型的复合数据结构,在下一篇文章会详细讲复合数据结构都有哪些。

红黑树是为了优化平衡二叉查找树,我们都知道平衡二叉树,在高度差大于1就会立即自旋,自旋对程序性能是有一定影响的,频繁的自旋会极大的降低程序的性能,红黑树就是抓住这一特点进行了优化,它也是一种自平衡二叉树,但是不会像AVL树那样频繁自旋。

之前专门写过一篇文章讲的红黑树,感兴趣的可以看一下JDK底层应用最多的 红黑树

image.png

特点:

  • 每个节点不是红色就是黑色
  • 不可能有连在一起的红色节点
  • 根节点都是黑色
  • 每个红色节点的两个子节点都是黑色的,叶子节点都是黑色的
  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

1.2.6 哈夫曼树

哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树。

基于哈弗曼树的特点,它可以用于最佳判定,哈夫曼编码(压缩)等场景

概念:

  • 路径:在一棵树中,一个节点到另一个节点之间的通路,称为路径。
  • 路径长度:在一条路径中,每经过一个节点,路径长度都要加 1 。例如在一棵树中,规定根节点所在层数为1层,那么从根节点到第 i 层节点的路径长度为 i - 1 。
  • 节点的权:给每一个节点赋予一个新的数值,被称为这个节点的权。
  • 节点的带权路径长度:指的是从根节点到该节点之间的路径长度与该节点的权的乘积。

举一个哈夫曼编码的应用

假设存在一段字符文本,AAABBCCAA... 我们可以先统计文本中文字出现的频率,得到如下频率表(字符:出现次数)

频率表 A:80, B:46, C:23 D:69 E:18 F:6 G:3

我们就可以得到如下哈夫曼树

image.png

将出现次数少的放在下边,将出现频率高的放在上边,然后左边路径设为0,右边为1,这样就可得到一张哈夫曼编码表。

A:11, B:01, C:000 D:10 E:0011 F:00101 G:00100

我们就可以用这种二进制编码表示字符,从而达到加密压缩的目的

1.3 非二叉树

1.3.1 B-树

B-树即B树,他也是一种多路自平衡查找树。

B树出现是为了实现高效 I/O的平衡二叉查找树。平衡二叉树的查找效率是非常高的,但是当数据量非常大,树的深度就会很大,这样就会造成磁盘I/O读写过于频繁,从而导致查询效率低下,并且数据量过大会导致内存空间不够容纳平衡二叉树所有节点的情况。B树是解决这个问题的很好的结构。

小知识

为啥说树的深度越大,磁盘IO会越平凡呢?

因为我们无法控制树的不同节点存储在相同的磁盘块中,访问不同的磁盘块就会加剧IO次数,所以我们通过降低树的深度,使其在查找数据时访问更少的磁盘块,这样就可以减少IO次数。

特点:

  • 每个节点最多只有m个子节点。
  • 每个非叶子节点(除了根)具有至少⌈ m/2⌉子节点。
  • 如果根不是叶节点,则根至少有两个子节点。
  • 具有k个子节点的非叶节点包含k -1个键。
  • 所有叶子都出现在同一水平(平衡)。

image.png

1.3.2 B+树

B+树又是一个被大家熟知的数据结构,MySQL中InnoDB索引的数据结构一般都是采用B+树的,我们都知道对数据库中的数据添加索引可以极大的提高读取数据的速度,B+树就是对B树的一种升级,使数据全部存储在叶子节点上,并且通过指针连接形成链表,这样可以极大的减小树的深度,还可以更好的范围查询。

MySQL中采用内存页的存储方式,一个内存页是16k,而一个节点是占一个内存页,所以数据全部存在叶子节点上可以,让中间层少存储数据,从而可以存储更多的指针,降低树的深度。

B+树对B树升级的点有:

  • 非叶子节点存储key,叶子节点存储key和数据
  • 叶子节点两两指针相互连接(叶子节点构成双向循环链表)便于排序,顺序查找

1.3.3 前缀树(Trie树)

前缀树又被称为前缀树、字典树。

前缀树典型应用是用于快速检索(最长前缀匹配),统计,排序和保存大量的字符串,所以经常被搜索引擎系统用于文本词频统计,搜索提示等场景。

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
  • 每个节点的所有子节点包含的字符都不相同

由下图可以清晰的看到前缀树的特点,这样不但能极大的节约内存,也能便于搜索。

image.png

2 图(Graph)

图是由顶点(Vertex)和边(Edge)构成

图分为有向图和无向图,还有带权图

image.png

邻接关系

  • 无向图

    邻接顶点是指图结构中一条边的两个顶点。

  • 有向图

    • 入边邻接顶点:连接该顶点的边中的起始顶点。
    • 出边邻接顶点:连接该顶点的边中的结束顶点。

往往我们在表示复杂的地图,铁道,公交路线,人物之间的复杂关系网等都可以使用图结构来表示。

图是相对复杂的数据结构,一般我们日常使用会很少,由图衍生出多种存储结构,如:邻接矩阵 、邻接表、十字链表、邻接多重表等等。

图的遍历是我们需要关注的。

遍历:

  • 深度优先搜索算法遍历(栈思想)
  • 广度优先搜索算法遍历(队列思想)
目录
相关文章
|
2月前
|
算法
数据结构之博弈树搜索(深度优先搜索)
本文介绍了使用深度优先搜索(DFS)算法在二叉树中执行遍历及构建链表的过程。首先定义了二叉树节点`TreeNode`和链表节点`ListNode`的结构体。通过递归函数`dfs`实现了二叉树的深度优先遍历,按预序(根、左、右)输出节点值。接着,通过`buildLinkedList`函数根据DFS遍历的顺序构建了一个单链表,展示了如何将树结构转换为线性结构。最后,讨论了此算法的优点,如实现简单和内存效率高,同时也指出了潜在的内存管理问题,并分析了算法的时间复杂度。
65 0
|
5天前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
38 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
5天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
31 12
|
5天前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
32 10
|
5天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
25 2
|
3月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
132 64
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
81 5
|
2月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
118 16
|
2月前
|
算法
数据结构之文件系统模拟(树数据结构)
本文介绍了文件系统模拟及其核心概念,包括树状数据结构、节点结构、文件系统类和相关操作。通过构建虚拟环境,模拟文件的创建、删除、移动、搜索等操作,展示了文件系统的基本功能和性能。代码示例演示了这些操作的具体实现,包括文件和目录的创建、移动和删除。文章还讨论了该算法的优势和局限性,如灵活性高但节点移除效率低等问题。
72 0
|
3月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
37 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)