前言

  一段程序最容易出错的就是在判断或者是情况分类的边界地方,所以,应该对于许多判断或者是情况分类的边界要格外的注意。下面,就分析下STL中红黑树的迭代器的各种边界情况。(注意:分析中STL使用的版本是SGI STL,由于不同的版本的STL具体实现细节不一样,所以可能会有出入)。

一、begin()获取第一个迭代器的自减

  begin()函数获取的是一个容器的首迭代器,指向容器中的第一个元素(这里的第一个不一定是指储存顺序(物理)上的第一个,一般是指逻辑上的第一个,在红黑树中是指树中的最左节点)。那么对于首个迭代器进行自减会怎样?

(1)首迭代器指向(即最左节点)不是根节点

最左节点不是根节点,就是根节点有左子树。那么第一个迭代器,指向一定是左子树的最左节点。--begin() ,会调用的decrement()函数,该函数如下:
   void decrement()
{
if (node->color == __rb_tree_red &&
node->parent->parent == node)
node = node->right;
else if (node->left != ) {
base_ptr y = node->left;
while (y->right != )
y = y->right;
node = y;
}
else {
base_ptr y = node->parent;
while (node == y->left) {
node = y;
y = y->parent;
}
node = y;
}
}
  因为第一个迭代器是执行最左节点,所以其没有左子树,node->parent->parent也不等于node,所以他会回溯,找到第一个node 不是其父节点的左节点,那么他的父节点就是其结果。由于最左节点上溯不可能会有节点不是其父节点的左节点,那么while的最后一个循环时,node指向根节点,y指向header节点,此时node != y->left,(y->left指向就是最左节点),那么循环结束node = y,即指向了header节点,也是end().
 

(2)首迭代器指向(最左节点)是根节点

  最左节点就是根节点,这就说明根节点没有左子树。此时--begin(),同样也是调用的decrement()函数,此时node->parent->parent != node,同时最左节点也没有左子树。那么函数会进入while循环,第一次y指向头结点(header指向的节点),node即是根节点,也是最左节点,循环条件满足,node == y->left(即最左节点),进入循环,node = y,node现在是头结点了,y = y->parent,指向根节点了,(头结点的parent是根节点,这是实现的技巧)。此时y->left,即根节点的左子节点,根没有左子树,所以为0,此时循环条件不满足,跳出循环,再node = y。即node又变为根节点了。即对于根没有左子树的红黑树,对--begin(),其结果还是begin()。

 iterator it = rb_tree.begin();
it == --it;

二、end()获取last迭代器的自减

  last = end(); last指向的是头结点(也这是红黑树实现中的技巧,header和end()都指向头结点)。此时--last,会调用decrement函数,此时node即是头结点,node->color == __rb_tree_red &&node->parent->parent == node,为真。(头节点的颜色规定为红,而且头节点的parent是根节点,根节点的parent又是头结点,所以node->parent->parent==node)。这时候node
= node->right。头结点的右节点,指向的红黑树的最右节点,最大的节点。此时--last,指向最大节点是正确的。

三、指向最右节点迭代器自增

  无论什么样的情况,自增都是调用的increment()函数,那么就先上increment函数吧,如下:

 void increment()
{
if (node->right != ) {
node = node->right;
while (node->left != )
node = node->left;
}
else {
base_ptr y = node->parent;
while (node == y->right) {
node = y;
y = y->parent;
}
if (node->right != y)
node = y;
}
}

(1)最右节点不是根节点

  这种情况,就是根节点还有右子树。这种情况下,对指向最右节点的迭代器自增,会调用上面increment函数。应该是指向最右节点,所以node->right == 0,所以只能上溯,找到其现行的节点不是其父节点的右节点。最右节点上溯,不可能会有现行的节点不是其父节点的右节点,while的最后一次循环时,node指向根节点,y指向头结点(即end()指向的节点),这时,node != y->right(存储的是最右节点),跳出循环。在判断node->right
!= y,现在node是根节点,其有右节点不是头结点,所以判断结果为真,node=y,最后node为y,即头结点,也就是end()返回的迭代器指向的节点,亦是last节点,指向最右的节点的迭代器自增,得到last迭代器,这是正确的。

(2)最右节点是根节点

如下图
此时,指向最右的迭代器自增,调用increment函数,node->right == 0,进入while循环。y开始是node的parent,即头结点,node == y->right(最右节点,此情况下即根节点),循环条件成立,进入循环。然后node = y,node变为头结点,y=y->parent(头结点的parent即根节点),y变为根节点。此时判断循环条件,y->right,即根节点的右子节点,这种情况下的根右子节点为空,node为头结点不为空,循环条件不成立,跳出循环。接下来是

       if (node->right != y)
node = y;

此时node是头结点,其右节点就是树的最右节点,这种情况下就是根节点,y就是根节点,那么if判断条件不成立,返回的时候node就是头结点,即end()返回迭代器指向的节点。此时迭代器就是last迭代器。对指向最右节点的迭代器自增,得到last迭代器,亦end()返回的迭代器,这是正确的。

结语

本文中讨论了各种边界情况,其中发现在SGI STL的红黑树中的一个有趣情况,对于迭代器first = begin(),其指向的节点是根节点的时候,(最左节点是根节点),对first自减得到的还是first。对于一些非边界的迭代器的自增和自减,不是这里讨论的范围。

SGI STL红黑树中迭代器的边界值分析的更多相关文章

  1. [转]SGI STL 红黑树(Red-Black Tree)源代码分析

    STL提供了许多好用的数据结构与算法,使我们不必为做许许多多的重复劳动.STL里实现了一个树结构-Red-Black Tree,它也是STL里唯一实现的一个树状数据结构,并且它是map, multim ...

  2. stl map底层之红黑树插入步骤详解与代码实现

    转载注明出处:http://blog.csdn.net/mxway/article/details/29216199 本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进 ...

  3. java中treemap和treeset实现(红黑树)

    java中treemap和treeset实现(红黑树)   TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 Tre ...

  4. 红黑树及其实例JDK中的TreeMap

    红黑树是一种自平衡二叉查找树(binary search tree,BST),红黑树是一种比较复杂的数据结构,红黑树查找.插入.删除元素的时间复杂度为O(log n),n是树中元素的数目.文章的要讲的 ...

  5. Java集合详解6:这次,从头到尾带你解读Java中的红黑树

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  6. 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树

    1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...

  7. Red Black Tree 红黑树 AVL trees 2-3 trees 2-3-4 trees B-trees Red-black trees Balanced search tree 平衡搜索树

    小结: 1.红黑树:典型的用途是实现关联数组 2.旋转 当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质.为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些 ...

  8. Sedgewick的红黑树

    红黑树一直是数据结构中的难点,大部分关于算法与数据结构的学习资料(包括<算法导论>)对于这部分的讲解都是上来就下定义,告诉我们红黑树这个性质那个性质,插入删除要注意1234点,但是基本没有 ...

  9. 红黑树(二)之 C语言的实现

    概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...

随机推荐

  1. BZOJ2668:[CQOI2012]交换棋子(费用流)

    题目描述 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. 输入输出格式 输入格式: 第一行 ...

  2. 【微服务】之七:轻松搞定SpringCloud微服务-API权限控制

    权限控制,是一个系统当中必须的重要功能.张三只能访问输入张三的特定功能,李四不能访问属于赵六的特定菜单.这就要求对整个体系做一个完善的权限控制体系.该体系应该具备针区分用户.权限.角色等各种必须的功能 ...

  3. MyBatis配置文件的配置说明

    Properties 1.创建一个资源文件jdbc.properties: jdbc.driverClassName=oracle.jdbc.driver.OracleDriver jdbc.url= ...

  4. postman提取返回值和断言

    将token接口中返回的access_token设置为全局变量. 如:token接口返回格式为: { "data": { "access_token": &qu ...

  5. 递归-归并排序 思想 JAVA实现

    已知一个数组   15.58.61.75.21.32.89.4.78.83.采用递归实现的归并排序将数组有序. 分治策略:(摘自<算法导论>) 在分治策略中,我们采用递归解决问题 分解:将 ...

  6. caffe/proto/caffe.pb.h: No such file or director

    caffe编译过程中遇到的为问题: fatal error: caffe/proto/caffe.pb.h: No such file or directory 解决方法: 用protoc从caffe ...

  7. Qt 学习之路 2(46):视图和委托

    Home / Qt 学习之路 2 / Qt 学习之路 2(46):视图和委托 Qt 学习之路 2(46):视图和委托  豆子  2013年3月11日  Qt 学习之路 2  63条评论 前面我们介绍了 ...

  8. php 实现无限极分类

    原始数据 $array = array( array('id' => 1, 'pid' => 0, 'n' => '河北省'), array('id' => 2, 'pid' ...

  9. LeetCode154.寻找旋转排序数组中的最小值 II

    154.寻找旋转排序数组中的最小值 II 描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). ...

  10. python-sort()/sorted()比较

    Sorting Lists sorted(iterable,key=None,reverse=False),does not mutate list, must assign result to a ...