1.二叉树的概念及结构
1.1概念
一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
特性:
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
1.2特殊的二叉树
满二叉树:每一层节点的顺序都是从左到右依次排列,其每一层节点的个数都到达最大。
完全二叉树:假设深度为K的二叉树,每一层的节点的顺序都是从左到右依次排序,除了第K层外每层节点的个数都达到最大,第K层节点的个数不一定达到最大。满二叉树是完全二叉树。
1.3二叉树的性质
1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 个结点.
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是.
3. 对任何一棵二叉树, 如果度为0其叶结点个数为, 度为2的分支结点个数为
,则有
=
+1.
2.堆
2.1堆的概念及结构
堆是一颗完全二叉树。堆有分为大根堆和小根堆。
2.2堆的实现(大根堆)
2.2.1结构体设定
堆是由动态数组来实现的,那里面肯定得由记录容量的一个变量,到达一定空间时就进行扩容,其次还要有一个变量记录堆里共由多少个数据。
代码实现:
2.2.2初始化
由于我们是用动态数组来实现的,故在初始化时可以为其开上一点空间,并对结构体的另两个成员进行初始化。
代码实现:
2.2.3插入
首先得判断是否需要扩容,接着再从尾部插入一个数据,然后在根据其父节点的值来进行调整数据,这里涉及到了一个向上调整的算法。
代码实现及图示:
向上调整算法:
条件:除了刚插入的那个数据(child)的位置,前面数据必须构成堆。
2.2.4删除
不是随便删除一个数据,而是删除堆顶元素的数据才有其意义,在堆中要想删除一个数据,不能直接删除,直接删除会导致大部分节点的关系紊乱。解决方法:将堆顶数据和最后一个数据进行交换,然后将其删除,最后在进行向下调整。
进行向下调整的条件:左右子树都构成堆。
代码实现及图解:
2.2.5堆顶数据
由于使用数组实现的,直接访问数组下标为零的数据即可。
代码实现:
2.2.6判断堆是否为空
结构体中有个记录堆的数据个数的变量size,当size==0,则堆为空。
代码实现:
2.2.7堆的元素个数
直接访问结构体中的size即可。
代码实现:
2.2.8销毁
当使用完时,应将开在堆上的空间释放掉,还给操作系统,还要对其结构体中的另外两个变量进行修改。
代码实现:
2.3堆排序
当我们对一组数据进行排序时,可以利用上面设计到的向上调整算法和向下调整算法,首先得先构成堆,然后再进行调整。一般采用向下调整建堆,其时间复杂度(O(N))优于向上调整建堆的时间复杂度()。
代码实现及解析:
2.4TopK问题
TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能 数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
1. 用数据集合中前K个元素来建堆
前k个最大的元素,则建小堆
前k个最小的元素,则建大堆
2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
代码实现及解析:
3.二叉树的链式结构实现
与堆的结构体不同,堆是完全二叉树用的是数组实现,链式结构体可以适合更多种情况,只要是符合二叉树性质就行。
3.1二叉树的遍历
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
3.1.1前序(DFS:深度优先遍历)
先访问根节点,在访问其左右子树。(根 左子树 右子树)
代码实现及图解:
递归展开图:通过画其递归展开图更容易理解
3.1.2中序
先访问左子树再访问根最后访问右子树。(左子树 根 右子树)
代码实现及图解:
3.1.3后序
向访问左子树再访问右子树最后访问根(左子树 右子树 根)。
代码实现及图解:
3.1.4层序(BFS:广度优先遍历)
利用队列作为辅助,队列中存储的数据应该改为结构体指针(节点的结构体指针)。主要原理:出上一层,带入下一层数据。
代码实现及图解:
3.2主要函数的实现
3.2.1二叉树节点的个数
利用递归实现。
代码实现:
3.2.2二叉树叶子节点的个数
代码实现:
3.2.3二叉树第K层节点的个数
递归实现。
代码实现及解析:
3.2.4二叉树查找值为x的节点
类似前序遍历,先判断根节点是否是要查找的值,若是则直接返回,不是再从左子树往下查找,若左子树也没人有,再从右子树中查找,直到所有节点寻找完。
代码实现及解析:
3.3二叉树的创建和销毁
3.3.1通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
代码实现及解析:
3.3.2二叉树销毁
将节点一个一个的释放掉,不能直接释就放根节点。
代码实现:
3.3.3判断二叉树是否为完全二叉树
层序遍历+完全二叉树的特性。
代码实现及解析:
希望对大家有所帮助,欢迎大家的指导与交流。
觉得不错的别忘记点赞+收藏咯,谢谢大家!