前言

  一段程序最容易出错的就是在判断或者是情况分类的边界地方,所以,应该对于许多判断或者是情况分类的边界要格外的注意。下面,就分析下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. how to use windows azure market

    here is the sample. namespace USCrime2006and2007 { class Program { static void Main(string[] args) { ...

  2. 安装 CentOs 系统 及 Python 及 Scrapy 框架

    1: 先安装Centos 系统: 为什么选择CentOs系统,而不选择Ubuntu ? 我在Ubuntu上尝试了三次安装 python 和 Scrapy ,结果都没成功,在运维老王的建议下 使用Cen ...

  3. 用python的正则表达式实现简单的计算器功能

    #!/usr/bin/env python # -*- coding:utf-8 -*- import sys import re def welcome_func(): ""&q ...

  4. c++11中关于`std::thread`线程传参的思考

    关于std::thread线程传参的思考 最重要要记住的一点是:参数要拷贝到线程独立内存中,不管是普通类型.还是引用类型. 对于传递参数是引用类型,需要注意: 1.当指向动态变量的指针(char *) ...

  5. HTTP上下文表单内容转为实体对象

    using ServiceStack.Web; using System; using System.Collections.Generic; using System.Linq; using Sys ...

  6. IO模型之三Reactor 和 Proactor IO设计模式

    反应器Reactor: 在事件驱动的应用中,应用中的请求总是通过事件(如CONNECTOR.READ.WRITE等)来表示,当多个请求同时到来时,这些请求最终还是会被序列化地处理,在序列化处理这些服务 ...

  7. shell-004:检测机器存活或者网络陡动情况!

    如下图情况,我们监测的就是此数据,当大于50%了,我们就可以设置告警等! #!/bin/bash # 用ping检测一台机器的存活或者网络波动情况 # 检测机器的丢包率来检测网络波动情况!! n=`p ...

  8. Qt 学习之路 2(70):进程间通信

    Qt 学习之路 2(70):进程间通信 豆子 2013年11月12日 Qt 学习之路 2 16条评论 上一章我们了解了有关进程的基本知识.我们将进程理解为相互独立的正在运行的程序.由于二者是相互独立的 ...

  9. springboot(五)-使用Redis

    Redis服务器 springboot要使用redis,首先当然要确保redis服务器能够正常跑起来. pom.xml 这里添加redis的依赖,当然也是springboot集成好的. <!-- ...

  10. Putty之public key ssh认证入门

    1.工作平台 客户端:Win2kEn Sp3,Putty Beta 0.53 服务器:RedHat72,OpenSSH_3.4p1 2.Putty简介 一个免费小巧的Win32平台下的ssh客户端.它 ...