背景
SGI STL红黑树实现上的特殊点,在于红黑树有一个header辅助节点;
- 该header的父节点为root节点;
- 其左节点为当前二叉树中的最小值(最左);
- 其右节点为当前二叉树中的最大值(最右);
- 该节点本身就正好用来当做end迭代器;
其初始化代码如下:
void init() {
header = new Node();
header->color = __rb_tree_red;
header->parent = nullptr;
header->left = header;
header->right = header;
}
问题
由于header辅助节点的存在,当我们使用迭代器进行“前进”、“后退”的操作时,我们需要考虑一些边界条件;
以前进为例,既然是前进,要考虑的边界无非两种:
- 迭代器当前指向了红黑树中的最后一个节点,下一个节点就是end了;
- 迭代器本身就已经指向end,但是请注意在C++中对end迭代器调用++的行为是未定义的,我们无需考虑;
基于上述两点考虑,我们可以写出如下的初步代码:
void increment() {
if (node == header->right) { // 边界条件1: 当迭代器目前已指向最后一个节点;
node = header;
return;
}
// 函数入口处的判断条件,保证了此处必然可以找到对应的正常节点(非header);
if (node->right != nullptr) {
node = node->right;
while (ndoe->left)
node = node->left;
} else {
node *parent = node->parent;
while (parent->right == node) {
node = parent;
parent = parent->parent;
}
node = parent;
}
}
稍微细看,我们就可以发现条件 if (node->right != nullptr) 本身就保证了,其必然可以从右子树中找到下一个正常节点;
因此,我们试着把边界处理放到else这个分支中;根据最后一个节点所在的位置,我们可以分两种情况进行考虑:
1. 最后一个节点在根节点的右侧;
2. 最后一个节点在根节点;
针对边界条件1, else分支中的代码:
当运行到 (node指向root,parent指向header时),parent->right != node,循环终止,node = parent符合预期效果;
针对边界条件2, else分支中的代码:
node指向root,parent指向header时, parent->right == node,因此node = parent(header), parent = parent->parent(root),二者交换位置;此时,node已经指向了header,无需额外赋值操作;
综上 ,我们需要一个判断条件,来决定是否执行node = parent 这个赋值操作;
另外,需要补充的一点就是,除了我们刚刚考虑的上述两种边界条件之外,正常情况下都是要node = parent赋值的;
因此,我们的判断条件,应该从边界条件2本身的特点出发,进行思考;
因此,我们写出如下的判断条件: if (header->right != root)
void increment() {
if (node->right != nullptr) {
node = node->right;
while (node->left)
node = node->left;
} else {
node *parent = node->parent;
while (parent->right == node) {
node = parent;
parent = parent->parent;
}
if (header->right != root) // Todo: SGI STL源码中是node->right != y 是否完全等价呢?
node = parent;
}
}
类似地,我们对decrement进行分析,“后退”存在的特殊情况就是:迭代器已经指向了end位置;
// 同理,针对first为止,使用decrement应该是未定义行为;
void decrement() {
if (node->color == _rb_tree_red &&
node->parent->parent == node)
node = node->right; // 当node为header时候(即迭代器已经指向end位置时);
else if (node->left != nullptr) {
base_ptr child = node->left;
while (child->right != nullptr)
child = child->right;
node = child;
} else {
base_ptr parent = node->parent;
while (node == parent->left) {
node = parent;
parent = parent->parent;
}
node = parent;
}
}