C++ STL和平衡二叉树红黑树

在实际使用过程中,到底选择这几种容器中的哪一个,应该根据遵循以下原则:

1、如果需要高效的随机存取,不在乎插入和删除的效率,使用vector;

2、如果需要大量的插入和删除元素,不关心随机存取的效率,使用list;

3、如果需要随机存取,并且关心两端数据的插入和删除效率,使用deque;

4、如果打算存储数据字典,并且要求方便地根据key找到value,一对一的情况使用map,一对多的情况使用multimap;

5、如果打算查找一个元素是否存在于某集合中,唯一存在的情况使用set,不唯一存在的情况使用multiset。
C++ STL和平衡二叉树红黑树_第1张图片序列式容器(array, vector, deque, list, forward-list),
关联式容器(set/multiset, map/multimap,unordered containers)
二叉树:只有两个分叉的树
数据结构书中的解释为度不超过2的树,何为度?度的意思是当前结点有几个分叉就是几度。那又何为结点呢?个人理解分叉的地方都是结点,树的叶子也是结点,称为叶子结点!
C++ STL和平衡二叉树红黑树_第2张图片C++ STL和平衡二叉树红黑树_第3张图片
先看图1: 节点有:A,B,C,D,E,F,G 叶子结点: D,E,F,G 其中结点A又被称为根节点。

               结点A,B,C分别都有两个子节点,所以他们的度为2,叶子结点D,E,F,G的度为0。

再看图2: 只是比图1多了一个结点T,他同样也是叶子结点,因为A多了一个子
性质1:非空二叉树中的叶子结点的数量等于双分支结点(度为2的结点)的数量加1.
性质2:二叉树的第i层上最多有(i>=1)个节点。
性质3:高度(或深度)为i的二叉树最多有(i>=1)个节点。也可以认为高度为i的二叉树有个结点,那么该二叉树为满二叉树。
平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。

具有以下特点:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。

二叉树的四种不平衡情况
C++ STL和平衡二叉树红黑树_第4张图片C++ STL和平衡二叉树红黑树_第5张图片1.LL:称为"左左"。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。

例如,在上面LL情况中,由于"根节点(7)的左子树(5)的左子树(3)还有非空子节点",而"根节点(7)的右子树(8)没有子节点";导致"根节点(7)的左子树(5)高度"比"根节点(7)的右子树(8·)"高2。

2.LR:称为"左右"。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。

例如,在上面LR情况中,由于"根节点(7)的左子树(4)的右子树(6)还有非空子节点",而"根节点(7)的右子树8)没有子节点";导致"根节点(7)的左子树(4)高度"比"根节点(7)的右子树(8)"高2。

3.RL:称为"右左"。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。

例如,在上面RL情况中,由于"根节点(7)的右子树(8)的左子树(6)还有非空子节点",而"根节点(7)的左子树(4)没有子节点";导致"根节点(7)的右子树(8)高度"比"根节点(7)的左子树(4)"高2。

4.RR:称为"右右"。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。

例如,在上面RR情况中,由于"根节点(7)的右子树(8)的右子树(10)还有非空子节点",而"根节点(7)的左子树(4)没有子节点";导致"根节点(7)的右子树(8)高度"比"根节点(7)的左子树(4)"高2。

1️⃣为什么有了平衡树还需要红黑树?
虽然平衡树解决了二叉查找树退化为近似链表的缺点,能够把查找时间控制在 O(logn),却不是最佳的。因为平衡树要求每个节点的左子树和右子树的高度差至多等于 1,这个要求太严,导致每次进行【插入/删除】节点的时候,几乎都会破坏平衡树的第二个规则,进而都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。

2️⃣红黑树的特性
显然,如果在【插入/删除】很频繁的场景中,平衡树需要频繁调整,这会使平衡树的性能大打折扣,为了解决这个问题,于是有了红黑树,红黑树具有如下特点:

每个节点非黑即红。
根节点总是黑色的。
每个叶子节点(NIL)都是黑色的。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点]
如果节点是红色的,则它的子节点必须是黑色的(反之不一定)。
从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑色节点。[这里指到叶子节点的路径]

包含 n 个内部节点的红黑树的高度是 O(log(n))。如图:
C++ STL和平衡二叉树红黑树_第6张图片
为什么要使用红黑树?原因:红黑树是一种平衡树,复杂的定义和规则都是为了保证树的平衡性。如果树不保证平衡性有可能变成链表:

C++ STL和平衡二叉树红黑树_第7张图片
在动图 5.2 中,节点 66 的左子树高度为 1,右子树高度为 3,此时平衡因子为 -2,树失去平衡。

在动图 5.2 中,以节点 66 为父节点的那颗树就称为 最小失衡子树。

最小失衡子树:在新插入的结点向上查找,以第一个平衡因子的绝对值超过 1 的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的。根据旋转的方向有两种处理方式,左旋 与 右旋 。

旋转的目的就是减少高度,通过降低整棵树的高度来平衡。哪边的树高,就把那边的树向上旋转。
行左旋操作,流程如下:

(1)节点的右孩子替代此节点位置 (2)右孩子的左子树变为该节点的右子树 (3)节点本身变为右孩子的左子树

右旋操作与左旋类似,操作流程为:

(1)节点的左孩子代表此节点 (2)节点的左孩子的右子树变为节点的左子树 (3)将此节点作为左孩子节点的右子树。

你可能感兴趣的:(面试,面试,c++)