IT技术博客大学习 共学习 共进步

STL笔记之二叉查找树

程序人生 2015-01-25 21:37:04 浏览 3,983 次

   SGI STL的关联容器(map、set、multimap、multiset)底层都是基于红黑树(Red Black Tree,RBT)来实现的,红黑树是一种被广泛使用的二叉查找树(Binary Search Tree,BST),有比较良好的操作效率。

   1. 二叉查找树

   二叉查找树可提供对数时间的元素插入和访问,其遵循以下规则:任何节点的键值一定大于其左子树中每一个节点的键值,并小于其右子树中每一个节点的键值。不过在极端情况下,当所有节点位于一条链上时,二叉查找树的操作时间为O(N)。二叉查找树样例:

   Binary Search Tree 二叉搜索树

   1.1 最大值与最小值

   根据二叉查找树的性质,最小值即沿着根节点不断沿着左子树下降,最后一个节点即为值最小的节点;同理最大值即沿着根节点不断沿着右子树下降,最后一个节点即为值最大的节点。

   1.2 节点的插入

   从根节点开始,如果新插入节点的值比当前节点的值要大,则选择右子节点继续对比,否则选择左子结点继续对比,这样操作直到叶节点。将新插入节点做为叶节点的子节点(根据大小关系设置为左子结点或者右子节点)。

   二叉查找树插入节点

   1.3 节点的删除

   根据被删除节点的不同,存在以下几种情况:

   1.3.1 被删除节点为叶子节点

   直接删除该节点即可(将父节点对应的左[右]子树设置为NULL);

   1.3.2 被删除节点只有左子树(或者只有右子树)

   将被删除节点的父节点指向被删除节点的指针指向被删除节点的子节点;

   1.3.3 被删除节点同时存在左右子树

   方法一:找到被删除节点的后继,将被删除节点的值更新为后继节点的值,最后删除后继节点;

   方法二:找到被删除节点的前趋,将被删除节点的值更新为前趋节点的值,最后删除前趋节点;

   下图展示了方法一删除方法:

   二叉搜索树删除节点

   1.4 CLRS 12.2-5

   证明:如果二叉查找树中的某个节点有两个子女,则其后继没有左子女,其前趋没有右子女。

   这个结论是1.3.3的一个支撑。可以用反证法证明:

   后继即右子树中值最小的节点,也就是右子树中的最左叶子节点,该节点肯定不可能还存在左子女,否则就不可能是最左的叶子节点了。同理可证前趋没有右子女。

   2. AVL树

   AVL树属于二叉查找树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n),增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

   二叉查找树的查找和插入操作在最坏情况下复杂度为O(N),而AVL树最坏时仍然为O(lgN)。

   2.1 节点的旋转

   AVL树的插入和删除操作需要借助于节点的旋转来保持树的高度平衡。节点的旋转分为左旋和右旋。旋转之后仍然保持二叉查找树的性质。

   二叉查找树节点左旋

   右旋操作时左旋操作的逆操作,对旋转操作只要记住保持BST的性质就很好理解了。

   2.2 节点的插入

   节点的插入结果可以分为四种情况。节点插入之后如果AVL树的平衡遭到破坏,那么,令X为平衡状态被破坏的节点中最深(下方)的节点。

   1. 插入点位于X的左子结点的左子树——左左;

   2. 插入点位于X的左子结点的右子树——左右;

   3. 插入点位于X的右子结点的左子树——右左;

   4. 插入点位于X的右子结点的右子树——右右;

   对于第1种和第4种情况,进行一次旋转即可保持AVL树的性质;对于第2种、第3种需要进行两次旋转操作来保持AVL树的性质。

   情况一:

   AVL树节点插入(左左)

   情况二:

   AVL树插入节点(左右)

   3. 红黑树

   如果BST满足如何红黑性质,则该BST为红黑树:

   1. 每个节点要么是红色,要么是黑色;

   2. 根节点为黑色;

   3. 如果一个节点为红色,则其子节点必为黑色;

   4. 从任一节点至NULL节点的任何路径,包含同样个数的黑节点;

   当给红黑树插入一个节点时,为了保证尽可能少的破坏红黑树的性质,我们让:新增的节点着为红色。此时如果插入节点后整棵树只有一个节点,那么性质2被破坏,只要把节点染为黑色即可;而如果新增节点的父节点也为红色,就破坏了性质3,这种情况下操作起来就有点复杂了。

   红黑树性质的保持

   如果插入一个节点后红黑树性质被破坏,我们需要进行一些操作来保持红黑树节点的性质。这里划分为四种情况(还有另外四种情况是对称的),先做如下约定:

   1. 新增节点:X

   2. 父节点:P

   3. 祖父节点:G

   4. 伯父节点:S

   5. 曾祖父节点:GG

   情况一

   S为黑且X为外侧插入,此时旋转PG并更改PG颜色:

   S为黑且X为外侧插入

   情况二

   S为黑且X为内侧插入,先对PX做一次单旋转并更改GX颜色,再对GX做一次单旋转:

   S为黑且X为内侧插入

   情况三

   S为红且X为外侧插入,先对PG做一次单旋转,并更改X的颜色,此时如果GG为黑,则完毕,否则进入情况四:

   S为红且X为外侧插入

   情况四

   S为红且X为外侧插入,先对PG做一次单旋转,并更改X的颜色,此时GG为红,继续往上做,直到不再有父子节点连续为红。

   S为红且X为外侧插入

   上述四种情况均为P为G的左子结点的情况,当P为G的右子节点时情况是类似的。

   STL源码剖析笔记系列

   1. STL笔记之空间配置器

   2. STL笔记之迭代器

   3. STL笔记之vector

   4. STL笔记之list

   5. STL笔记之优先队列

   6. STL笔记之deque

   7. STL笔记之slist

   8. STL笔记之hashtable

   9. STL笔记之二叉查找树

建议继续学习

  1. 关于使用STL的红黑树map还是hashmap的问题 (阅读 8,741)
  2. 数据结构定义中的中(大陆地区)美差异 (阅读 6,760)
  3. 萃取(traits)编程技术的介绍和应用 (阅读 6,364)
  4. 数据映射–平衡二叉有序树 (阅读 4,441)
  5. 一个简单的stl中string的split函数 (阅读 4,262)
  6. 二叉树迭代器算法 (阅读 3,980)
  7. 小趣闻:STL的三个版本 (阅读 3,783)
  8. 数据结构之treap (阅读 3,540)
  9. 倒置字符串中的单词 (阅读 3,484)
  10. STL笔记之hashtable (阅读 3,382)