二叉树的基本理解

二叉树概念及结构

概念
一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 由一个根结点加上两棵别称为左子树和右子树的二叉树组成
718ab85f357d48d4805bab0045247357.png
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
二叉树一般只有以下几种情况
6ff398835856479cb80f7ebb8fabe9ee.png
特殊的二叉树:
1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
一般也是使用这几种二叉树,后续也会有判断一类的代码。
bd546e4c701749a592d078f347a89167.png

二叉树的两种储存结构

顺序储存
概念
顺序结构存储就是使用 数组来存储,一般使用数组 只适合表示完全二叉树,因为不是完全二叉树会有 空间的浪费。而现实中使用中只有堆才会使用数组来存储,
就用来表示顺序储存。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树
要注意:
现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
堆的概念
如果有一个关键码的集合K = { K1,K2 ,K3 ,…,K(n - 1) }, 它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:

K(i) <= K(2*i +1) 且K(i) <= K(2*i +2) (K(i) >= K(2*i +1) 且K(i) >= K(2*i +2)) i = 0,1,2…,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

堆的性质:
  • 堆中某个结点的值总是不大于或不小于其父结点的值;(上述概念中满足的条件就是要保证此性质)
  • 堆总是一棵完全二叉树。

堆的创建
一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。
若根结点左右子树不是堆,我们怎么调整呢?
这里我们从倒数的第一个非叶子结点的子树开始调整,一直调整到根结点的树,就可以调整成堆。即上调整算法;

432473e587eb422ea76661691f74ded0.pngf34b2a49a4154f3aa5291f89b4bdf044.png514f85831d774f0cb975e280af5e23d0.png

5f70a984406243d5b744258a44e9b33f.pngbe215d7a8ea44041b2e242939bbf24ee.png

从图中可以看到,第三步完成后,根节点左子树的归结构被破坏,则每次调换元素时都有可能破坏其子堆;因此每次进行元素调换后,都要进行递归调用重新构建堆的结构。即第四步;如此一来代码的实现十分麻烦,因此多用一下方式创建一个堆

如小堆(函数的内容与实现,下面,代码实现部分)

da40cb1ba3ad46e1acd050b787caab94.png

建堆1:即从下标为一的位置开始向上调整,直到最后一个。

82d8008694484f64bae1daad455819b0.png  19 > 15 不用调整

34ea4493632e485d803290a8c845be47.png

00155af62b324e13b4886d05da2032e9.png 28 > 18  34 > 19  65 > 19  49 > 27   不用调整

0c5818df4d504f0ab225db09db91db39.png  37 > 28 不用调整

建堆2:从第一个非叶节点的位置开始向下调整,直到调整到根节点。

7e940562738d456089dddefe52979776.png 其中  37 > 28   25, 49 > 18   34, 65 > 19  18, 28 > 15 

                                                                               

ce9aafdbfec14d6584c7cad6db44831d.png

堆的插入
先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。
如图:

a5b28f96070f4fb5b05f221f1891693f.png1c140fe0fa474a4cb59484c98df7d217.png

17f5748340e34908b9633b5783c782da.png9c1e1aa563f24c178d83f777395fd7e4.png

大致可分为两部

  1. 先将元素插到堆末尾,即最后一个孩子;
  2. 插入后若堆的性质招到破坏,那就顺着新节点的双亲向上调整直至合适为止;(代码实现和创建类似)
堆向下调整算法
向下调整算法有一个前提: 左右子树必须是一个堆,才能调整。
如现给一个数组逻辑上看作是一个完全二叉树;通过根节点的向下调整可以把此堆调整为,小堆;
int array[] = {27,15,19,18,28,34,65,49,25,37};
da64669c08244ea2a8b9e0753486300e.png
3aa1a332894445f1b7cd53abd9b75c3e.png
堆的删除

堆的删除要利用向下调整算法

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
54e6cd4ec0cd441eb814c5a2c5ec8b22.png 2824ab0044ca4711b8081e9c964c2d2e.png 2c7c94a1ad324bcabf01e7665da43800.png

代码的实现

头文件 Heap.h

62590468fc8c4c669e7e497824cda3ec.png

源文件Heap.c

f9aeb8c4181f4891a50388710e9cb1ee.png

二:链式储存

前置说明
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于c语言自动创建二叉树很难,因此 此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习。
c834e37bba10479fbedd1364e2bceccb.png
注意:上述代码并不是真正创建二叉树的方式。
复习二叉树的概念
二叉树是:
1. 空树
2. 非空:根结点,根结点的左子树、根结点的右子树组成的。

所以二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。
二叉树的遍历
所谓 二叉树遍历 (Traversal) 是按照某种特定的规则,依次对二叉 树中的结点进行相应的操作,并且每个结点只操作一次。访问结点所做的操作依赖于具体的应用问题。
前序/中序/后序的递归结构遍历
按照规则,二叉树的遍历有: 前序 / 中序 / 后序的递归结构遍历
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
由于被访问的结点必是某子树的根, 所以 N(Node )、 L(Left subtree )和 R(Right subtree )又可解释为 根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
以前序遍历为例子,遍历下列二叉树
36c389eab74245c983c4e1df1b806a15.png
e56e216c861044b19cc9e3efca3bdff2.png 9bb174986af34bf79defe5dcfd017f9a.png f1827ec7c0bc4a8aa95a485c33c7261a.png 53b5c239f3e4490caa7cab965c47c2fa.png 4f86aaa0e3c142989bc1700efa0dea03.png
559dcf96c6ba40248fc76459da5144a7.png a7d723cb914348aea7a9aff2f22a589f.png 7d1b4947025040df9688abe949a12150.png 6a6855a8838348e4829eac8e73729e97.png 5d67303e136e49f7813a0900df76792f.png
b26a2fbbc61a423f893486bef17f0e87.png 84440df8f24e42ab8a8a1e8385ff0d54.png 8c4b9c753c9842748e6eaef8d1230723.png 6010f2d9473741b99c1bdb444393d31d.png
前序遍历结果:1 2 3 N N N 4 5 N N 6 N N
当然 也可以用递归解释
前序遍历是这样的,大家可以自己试着写 中序 和 后序。答案在下面
中序遍历结果:{([N 3 N] 2 N) 1 ([N 5 N] 4 [N 6 N])}   
(如此也更好理解,一个括号内包含根 左子树 右子树)
后序遍历结果:N N 3 N 2 N N 5 N N 6 4 1
层序遍历

又称广度优先遍历(BFS) 

除了前序遍历、中序遍历、后序遍历(DFS)外,还可以对二叉树进行层序遍历。设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
c57d4b5b01b1455d9892fb3bef646292.png
理解:
可把层序遍历看成 队列,  上一层出来时 代入下一层。
03c69b3e4c834e70b30ce6be9556b6e5.png 82f8a37fe2e24fe8a9e1e7c11a4669eb.png adcec0dcda724cbc90175cbbb36b4b18.png 57195b6876554e208a0c1d347f96bce5.png d67e0c724c0b48be84ed7605db9c5adc.png
以此类推,出去一个若左右子树不为空带入,为空则不带入;
二叉树结点个数

递归思想

099f7e6d5ba54faba6f5a533e0df5918.png0f9c8e89f49c4e4c94888c75ebfc8403.png

root == NULL ? 0 ; 函数名(root->right) + 函数名(root->left) + 1

418d93b0779242da828201183f4a09a9.png

二叉树叶子结点个数

即 左右子树 都为空的节点个数

f1dc76568c7d44b88690f786dab1686e.png

二叉树第k层结点个数

k = 1 即 为 根时是一个节点;

把k层每个节点都看为根即可;

0d76558042bd4548b51d631c933fc746.png

二叉树查找值为x的结点

用递归找到x的值,若找到,先用一个代数保存在返回;若没找到,即已找到NULL,不返回。

二叉树销毁

递归思想依次销毁

判断二叉树是否是完全二叉树

与层序遍历相似,在程序遇到第一个NULL根时,若队内还有数,则不是完全二叉树。相反若无数了行,则是完全二叉树

代码实现
前,中,后序遍历

45bace55a64b4c6a9a9081554234a4fe.png

63c9d58e194444b0961a874ae8462ab2.png

8ed548766eaa41bab0cdd59297ee0081.png

层序遍历

(其中用到了队列,在我的内可以找到)

73dbb3c69de2438abe178958d584e09e.png

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值