二叉树
二叉树的存储方式:链式存储
1.1 满二叉树
和名字一样,满的
1.2 完全二叉树
底部从左到右一定连续,但不一定满
堆 是一个 完全二叉树,C++ STL中的优先级队列(小顶堆或大顶堆)就是基于完全二叉树。
1.3 二叉搜索树
二叉搜索树是一个有序树。
二叉搜索树不能有重复元素
1 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
2 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
3 它的左、右子树也分别为二叉排序树
1.4 平衡二叉搜索树
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树(红黑树就是一种二叉平衡搜索树),所以map、set的增删操作时间时间复杂度是logn,
PS:unordered_map、unordered_set,unordered_map、unordered_set底层实现是哈希表。
红黑树
红黑树(Red-Black Tree)是一种平衡二叉搜索树,它在插入和删除节点时通过一系列的旋转和重新着色操作来保持平衡。红黑树在计算机科学中被广泛应用于高效地实现有序集合和映射等数据结构,C++ STL中的std::map和std::set就是基于红黑树实现的。
红黑树要点:
-
二叉搜索树性质:红黑树是一种二叉搜索树,即对于树中的每个节点,它的左子树上的所有节点都比它小,右子树上的所有节点都比它大。
-
节点颜色:每个节点都被标记为红色或黑色。初始时,所有节点都被着色为黑色。
-
根节点和叶子节点:根节点是黑色的,叶子节点(NIL节点或空节点)被视为黑色。
-
红黑规则:红黑树必须满足以下规则:
(R1) 节点是红色或黑色。
(R2) 根节点是黑色。
(R3) 叶子节点(NIL节点)是黑色。
(R4) 红色节点的两个子节点都是黑色。
(R5) 对于每个节点,从该节点到其后代叶子节点的所有路径上,包含相同数量的黑色节点。 -
自平衡操作:通过插入和删除操作来维持红黑树的平衡。在插入和删除时,根据具体情况进行节点的旋转和颜色调整,以确保满足红黑规则。
-
平衡性质:由于红黑树的高度始终保持在O(log n)范围内,其中n是树中节点的数量,因此它保证了高效的搜索、插入和删除操作,具有良好的平衡性质。
总之,红黑树是一种高效的自平衡二叉搜索树,通过保持一些特定的颜色和结构规则,它能够在动态操作中维持良好的平衡性质。这使得红黑树在实际应用中非常有用,特别是当需要高效地插入、删除和搜索有序数据时。
AVL树
(1)简介
AVL树是带有平衡条件的二叉查找树,一般是用平衡因子差值判断是否平衡并通过旋转来实现平衡,左右子树的高度差不超过1,和红黑树相比,AVL树是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差的绝对值不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而旋转是非常耗时的,由此我们可以知道AVL树适合用于插入与删除次数比较少,但查找多的情况。
(2)局限性
由于维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,更多的地方是用追求局部而不是非常严格整体平衡的红黑树。当然,如果应用场景中对插入删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树。
(3)应用
1.Windows NT内核中广泛存在;
B-树
底层数据结构:M阶平衡树
B-树是一种自平衡的搜索树,用于处理大量数据和磁盘访问。它具有以下特点:
- 每个节点可以容纳多个键值对,并且可以拥有多个子节点。
- 所有叶子节点位于同一层,通过链表连接起来,这样可以提高范围查询的效率。
- 节点的子节点数目范围为 [m/2] 到 m,其中 m 是树的阶数。
B-树适用于磁盘存储,因为它能够最小化磁盘访问次数。
搜索的时间效率都是log2 n 二分查找
B+树
- 所有键值都存储在叶子节点,内部节点只存储键值的副本和子节点的指针。
- 叶子节点通过链表连接,提供范围查询的效率。
- B+树通常用于数据库系统的索引结构,能够提供高效的范围查询和顺序访问。
B*树
设计目标:减少B-树的分裂和合并操作。
- B*树中的非叶子节点至少填满2/3的键值对,而不是B-树中的1/2。
- B*树在插入和删除操作时,倾向于将数据插入到已经有2/3填满的节点中,从而减少节点分裂的频率。
- B*树在内部节点中存储了部分叶子节点的键值对,这样可以减少对叶子节点的访问次数。
Kd-tree
k维的二叉树。其中的每一个节点都是k维的数据,数据结构如下所示
struct kdtree{
Node-data - 数据矢量 数据集中某个数据点,是n维矢量(这里也就是k维)
Range - 空间矢量 该节点所代表的空间范围
split - 整数 垂直于分割超平面的方向轴序号
Left - kd树 由位于该节点分割超平面左子空间内所有数据点所构成的k-d树
Right - kd树 由位于该节点分割超平面右子空间内所有数据点所构成的k-d树
parent - kd树 父节点
}
kd-tree在日常使用中,一般会在两个方面使用:
最近邻搜索
距离范围搜索
距离范围搜索的原理和最近邻搜索的差不多,把满足距离的全部放进去就可以了。
最近邻搜索的函数在激光点云匹配中找最近点的时候用的比较多:
#include <pcl/kdtree/kdtree_flann.h>
//设定kd-tree的智能指针
pcl::KdTreeFLANN<pcl::PointXYZI>::Ptr kdtreeCornerLast(new pcl::KdTreeFLANN<pcl::PointXYZI>());
//输入三维点云,构建kd-tree
kdtreeCornerLast->setInputCloud(laserCloudCornerLast);
//在点云中寻找点searchPoint的k近邻的值,返回下标pointSearchInd和距离pointSearchSqDis
kdtreeCornerLast->nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance);
其中,当k为1的时候,就是最近邻搜索。当k大于1的时候,就是多个最近邻搜索。
距离范围搜索:
//在点云中寻找和点searchPoint满足radius距离的点和距离,返回下标pointIdxRadiusSearch和距离pointRadiusSquaredDistance
kdtreeCornerLast->radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance)
iKd-tree
kd-Tree只用新的即将到来的点增量地更新k-d树,这导致的计算时间比现有的静态k-d树要低得多。除了点级操作外,ikd-Tree还支持一些特性,如箱形操作和降采样,这些特性在机器人应用中实际有用。与增量操作(即插入、重新插入和删除)并行,ikd-Tree主动监视树结构并部分重新平衡树,这可以在后期阶段实现有效的最近点搜索。ikd-Tree经过精心设计,支持多线程并行计算,以提高整体效率。我们在理论和实际实验中都验证了ikd树的有效性。在理论层面上,提出了一个完整的时间复杂度分析,证明了其有效性。在实验水平上,在激光雷达-惯性测程和映射应用中,在随机数据集和真实世界的激光雷达点数据上对ikd-树进行了测试。在所有测试中,ikd-Tree只消耗了静态k-d树中运行时间的4%。