什么是树
![80a667a3eb8b935031200e2bfcdf3d8b.png](https://i-blog.csdnimg.cn/blog_migrate/9a0a05a15f0d2ed2928b9392bd5e9c81.jpeg)
没错,就是这样的!一眼不就看出来了,有根、主干和叶子,组成就这样简单。
别人这么理解一点毛病都没,我们作为程序猿的要是碰到某个烦人的面试官这么说就GG了。
树的简介
在计算器科学中,树(英语:tree)是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。是一种非线性的数据结构,由n(n >=0)个结点组成的有限集合。如下:
![37f4fb05023abe816855cb79c8cba485.png](https://i-blog.csdnimg.cn/blog_migrate/e9156e79fcb575bece673901789ee104.jpeg)
看得出来,每个节点都是和另一个节点有关系的。A-> B C D,B->E F。。。。
我们先理解树组成的名称代表什么意思。
结点:使用树结构存储的每一个数据元素。例如元素A和B都代表一个节点。
根结点:每一个非空树都有且只有一个被称为根的结点。例如A为本颗树的根节点。
叶子结点:该节点下没有任何子节点。例如 K、L、F、G等称为叶子节点。
树度:对于一个结点,拥有的子树数(结点有多少分支)。叶子节点高度为1。例如K的度为1,D的度是3.
子树:我们从节点上来看,B、C、D也是一颗树,B、C、D作为各自树的根节点,这种树我们理解为子树。讲白了,每个节点都是一棵树,它自己就是根节点。
空树:没有结点的树。
森林:由n个子树构成的集合。
java构成一棵树的简单属性:
public class TreeNode { T value; //节点值 TreeNode leftChild; //左节点 TreeNode rightChild; //右节点 TreeNode(T value) { this.value = value; }}
数据结构当中有很多种树的结构,我们就来分析常见的二叉树、红黑树、B树、B+树是什么实现的。
二叉树
指每个结点最多有两颗子树,结点的度最大为2的树结构。性质如下:
- 层次从0开始,所以二叉树的第i层至多有2^i个结点(i>=0)。
- 高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1)(空树的高度为-1)。
- 任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n, 则m = n + 1。
二叉树通常有5种结构:
![4bfde0153c9a65dff6b6e00d7b71030f.png](https://i-blog.csdnimg.cn/blog_migrate/2a09e63a950161aa3fc94858928bc22a.jpeg)
依次为空树、一个根节点树、左子树(斜树)、右子树(斜树)、完全二叉树。
1)满二叉树
指所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。主要特点:
- 叶子只能出现在最下一层。
- 非叶子结点的度必须是2。
- 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
![f81257755b8c6edf9782ce4a1c247357.png](https://i-blog.csdnimg.cn/blog_migrate/fa25319bde1ad0ee9b98942029855992.jpeg)
2)完全二叉树
指除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树,主要特点:
- 该树非最后一层的结点都是满的。
- 最下层的叶子结点集中在树的左部。就是说如果树的某个结点度为1,则该结点只能有左节点,没有右节点。
![8f18b4f49618d01e55d2a55804b5078d.png](https://i-blog.csdnimg.cn/blog_migrate/9221925f068637950e8f9a804335efe7.jpeg)
可以看出满二叉树一定是一颗完全二叉树。反过来就不一定啊。
3)平衡二叉树
也叫AVL树。是指左子树上的所有节点的值都比根节点的值小,而右子树上的所有节点的值都比根节点的值大,且左子树与右子树的高度差最大为1。
![28df4e5857ffefc027394b109b9afc4a.png](https://i-blog.csdnimg.cn/blog_migrate/d647e6f265e85b3f0f0a7ef18a818564.jpeg)
3)二叉树的存储结构
顺序存储:即对树中的每个结点进行编号,然后以各结点的编号为下标,把各结点的值对应存储到一个一位数组中。获取下标的方式为:若一个结点的编号为i,则左、右孩子的编号分别为2i和2i+1。
如对上图中的满二叉树结构进行存储,得到的数组为:
![e9e9ff5b5b8c96559f787f2198c742c7.png](https://i-blog.csdnimg.cn/blog_migrate/e733e6de8dcee808ea339673d3338753.jpeg)
由于存储结构下标获取决定元素的位置,这种方式只适用于完全二叉树。
链式存储:通常每个节点有3个属性,值、左指针、右指针。想象下LinkedList。
![e86450e9672b27ce44318fd055d33038.png](https://i-blog.csdnimg.cn/blog_migrate/afadd947b57926b5ab334ae6dadca6b2.jpeg)
红黑树
是一种自平衡二叉查找树。是一种特化的AVL树(平衡二叉树,上有介绍)。查询、增加、删除时间复杂度都是O(logN),可见它的效率还是蛮高的。这里有没有想到HashMap从JDK1.8后底层链表结构在一定条件下采用了红黑树。特点:
- 节点必是红色或黑色。
- 根节点是黑色。
- 所有叶子都是黑色(叶子是NIL节点)。
- 如果一个结点是红的,则它的子结点必须是黑色的。
- 每个结点到叶子结点NIL所经过的黑色结点的个数一样的。
![2cb13d6342de1c178777ee29ad2c37aa.png](https://i-blog.csdnimg.cn/blog_migrate/5b712e98d39ea3579edda54a22f7f850.jpeg)
二叉查找树的由来就是因为平衡二叉树有个缺点就是插入和删除会破坏掉这个树的平衡,由于这种特性存在,然后就通过红黑树来实现。
红黑树的基本操作是添加、删除。进行添加或者删除后,红黑树的结构会发生变化,可能不再是一颗红黑树了。所以呢红黑树进行操作完成后,都要进行旋转。
左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,其左子结点保持不变。
右旋:与左旋相反的。
我裂开了!!!
红黑树的添加步骤:
- 如果是空树,那么直接让root等于这个新开节点,然后把这个节点的颜色变为黑色。如果不是空树,那么就先去找要插入的位置,如果要插入的值比该节点小就去左边,比该节点大就去右边。
- 找到位置之后将该节点连接到树中,然后将这个新插入的节点设置为红色。
- 如果插入这个红色节点之后父亲也是红色,则对这颗树进行调整(旋转)。
B树
全称Balance-tree(平衡的m路查找树),其中 m>=3。也是平衡二叉树的扩展。不同之处在于B树具有更大的扇出数(即更多的子节点)和更低的树高。节点是有序排列的。特点:
- 根节点至少有两个子节点。
- 每个节点有M-1个key,并且以升序排列。
- 位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间。
- 其它节点至少有M/2个子节点。
![fe8efaf91e5c26d1401b3b1d86d4cbec.png](https://i-blog.csdnimg.cn/blog_migrate/1eda4c4cc1c1a5d61e80abdf88a4ede3.jpeg)
1)结构
根节点:根节点没有父节点,位于树的最顶部。
叶子节点:树的最底层节点,而且没有任何子节点。
内部节点:所有连接根节点和叶子节点的节点,通常树包含多层内部节点。
2)查询
B树查找和二叉树的查找很相似。分为两步:
1、查找节点,由于B树数据通常是存储在磁盘上的,这步需要进行磁盘IO操作。
2、查找关键字,当找到某个节点后将该节点读入内存中然后通过顺序或者折半查找来查找关键字。若没有找到关键字,则需要判断大小来找到合适的分支继续查找。
B树查询的复杂性需要从两个方面考虑:磁盘页(B树是一种磁盘页面组织技术,通常一个节点即是一个磁盘页面。)传输的数量和查找时进行键比较的数量。在磁盘页传输数量方面,每个节点的分隔键划分当前搜索空间为原来的1/N。因此在从根节点到叶子节点的遍历过程中,需要读取磁盘页的数量,这个数量是B树的层数,即B树的高度M。
所以查询复杂度为log(M)。
B+树
是B树的一种变形树,也是一种多路搜索树。一个m阶的B+树相比较B树有以下特点:
有k个子结点的结点必然有k个关键码(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。![c11e3f39dd99087b3500bdf3630d6166.png](https://i-blog.csdnimg.cn/blog_migrate/e9e0efe59e789165f653d5b4b0a22bf5.jpeg)
查询:
在B+树中,只有叶子节点带有卫星数据(索引元素所指向的数据记录),而B树中的所有节点都带有卫星数据。查询流程和B树大致相同,主要不同点在:
- B+树的中间节点没有卫星数据,所以同样大小的磁盘页可以容纳更多的非叶节点,在数据量相同的情况下,B+树的结构比B树更“矮胖”,因此查询时IO次数更少。
- B+树的查询最终查找到叶子节点,而B树的查询可能匹配到叶子节点也可能匹配到中间节点,不稳定。
B+树优点主要体现:
- 由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。
- B+树的叶子结点都是相连的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性B+树更好。
总结
个人对数据结构树结构知识理解太少了,理解的东西不是很多。没有用程序去写示例出来,大家可以自己写案例尝试实现。然后像红黑树的旋转、数据插入等操作最好是手画步骤图去理解。
虽然说了这几种常见的树行结构,但是还有很多知识点上面都没体现出来,要是每个结构都写的很清楚,怕是要写上半个月,笔者自己看这些都要裂开了。还有其他的知识点比如数据插入分裂处理,数据的删除操作等都挺复杂的,这个要想全部都了解得花一定的时间才行。
明天周末了,放松下~~~