C语言数据结构与算法(二叉树)

1.二叉树的概念及结构

1.1概念

一棵二叉树是结点的一个有限集合,该集合:

1. 或者为空

2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

特性:

1. 二叉树不存在度大于2的结点

2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

1.2特殊的二叉树

满二叉树每一层节点的顺序都是从左到右依次排列,其每一层节点的个数都到达最大。

完全二叉树假设深度为K的二叉树,每一层的节点的顺序都是从左到右依次排序,除了第K层外每层节点的个数都达到最大,第K层节点的个数不一定达到最大。满二叉树是完全二叉树。

1.3二叉树的性质

1. 若规定根节点的层数为1,则一棵非空二叉树的i层上最多有 2^{i-1}个结点.

2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^{h}-1.

3. 对任何一棵二叉树, 如果度为0其叶结点个数为n_{0}, 度为2的分支结点个数为n_{2} ,则有 n_{0}n_{2}+1.

4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度为 h=\log _{2}(n+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))优于向上调整建堆的时间复杂度(n\log _{2}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判断二叉树是否为完全二叉树

层序遍历+完全二叉树的特性。

代码实现及解析:

希望对大家有所帮助,欢迎大家的指导与交流。

觉得不错的别忘记点赞+收藏咯,谢谢大家!

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值