STL源码分析之RB-tree结构简析
来源:互联网 发布:宁波seo教程 编辑:程序博客网 时间:2024/05/21 10:40
所谓关联容器:
每笔数据(每个元素)都有一个键值(key)和一个实值(value)。当元素被插入到关联式容器中时,容器内部结构(可能是RB-Tree 也可能是hash-table)便依照其键值大小,以某种特定规则将这个元素置放于适当的位置。
关联容器没有所谓的头尾(只有最大元素和最小元素),所以不会有所谓的push_back(), push_front(), pop_back(), pop_front(), begin(), end()这样的行为
一般而言,关联式容器的内部结构是一个平衡二叉树,以便获得良好的搜寻效率。二叉平衡树有许多种类型,包括 AVL-tree, RB-tree, AA-tree, 最被广泛用在STL的是RB-tree
路径长度(length): 根节点至任何节点之间有唯一路径,路径所经过的边数深度(depth): 根节点至任一节点的路径长度 根节点的深度永远为0高度(height): 某节点至其最深子节点(叶子节点)的路径长度节点大小(size): 其所有子代的节点总数
RB-tree结构设计
为了有更大的弹性,节点分为了两层。
//第一层//可见为了方便,将红黑两色设定为true和falsetypedef bool__rb_tree_color_type;const__rb_tree_color_type __rb_tree_red = false;const__rb_tree_color_type __rb_tree_black = true; //底层的红黑树节点(即不包含数据的树)//一层结构体还是很容易理解的//作为一棵二叉搜索树,最小值和最大值是很容易取得的struct __rb_tree_node_base{ typedef __rb_tree_color_type color_type; typedef __rb_tree_node_base* base_ptr; color_type color; base_ptr parent; base_ptr left; base_ptr right;//返回最小元素 static base_ptr minimum(base_ptr x) { while (x->left != 0) x = x->left; return x; } //返回最大元素 static base_ptr maximum(base_ptr x) { while (x->right != 0) x = x->right; return x; }};
//第二层template <class Value>struct __rb_tree_node : public __rb_tree_node_base{ typedef __rb_tree_node<Value>* link_type; Value value_field;};
RB-tree的迭代器
//第一层迭代器
struct__rb_tree_base_iterator{//在第一层RB-tree结构体中typedef__rb_tree_node_base* base_ptr; typedef __rb_tree_node_base::base_ptr base_ptr; typedef bidirectional_iterator_tag iterator_category; typedef ptrdiff_t difference_type; base_ptr node; //这个即使找下一个节点的意思,用于operate ++ void increment() { if (node->right != 0) { //右子存在,即找右子中在最左 node = node->right; while (node->left != 0) node = node->left; } else { //右子不存在,即找某个父节点 base_ptr y = node->parent; while (node == y->right) { node = y; y = y->parent; } //以上的循环我们可以理解,但是y不是一直是node节点的parent吗? //普通情况下,node->right是肯定不会等于y的,所以最终node也会成功指向要寻找的节点 //但是,我们这个RB-tree是有个header的 //以下我们会画图理解 if (node->right != y) node = y; } } //用于operate-- void decrement() { if (node->color == __rb_tree_red && //这种情况可能较难理解,等下看图 node->parent->parent == node) node = node->right; else if (node->left != 0) { //左子存在,即找左子的最右 base_ptr y = node->left; while (y->right != 0) y = y->right; node = y; } else { base_ptr y = node->parent; //否则找某个父节点 while (node == y->left) { node = y; y = y->parent; } node = y; //成功找到目标节点 } }};
要理解上面两个函数的两个难点,我们就要看下这个RB-tree的特殊之处导致的特殊情况:
可见,RB-tree是有一个不装数据的header的,左子指向最小,右子指向最大,parent指向root。
且header颜色是红的
难点一:
当我们调用increment函数时候,当前node即为root节点,此时想要寻找root的下一个节点。发现root节点并没有右子,只能向上寻找。想要找到以子节点是父节点左子节点的父节点作为返回值。因为此时特殊情况header的右子指向root,因此header作为当前节点继续往上寻找。因为header的parent为root,此时root的右子并不等于header,循环结束。此时出现的特殊情况是header也不是root的左子,反倒是node->right != y,即header->right=root,这种情况下node也不再需要等于y,因为我们的目标返回节点就是header
难点二:
当我们调用decrement函数时候,当前node为header(或者说是end()),该节点的parent的parent即是本身,我们要求返回的就是根节点。否则按照正常的逻辑来的话,会返回节点7。假设返回的是7,那么节点7调用decrement函数返回到header,header返回的又是节点7,就不合常理了
RB-tree的真正迭代器:
template <class Value,class Ref, class Ptr>struct __rb_tree_iterator: public__rb_tree_base_iterator{ 。typedef Value value_type; typedef Ref reference; typedef Ptr pointer; typedef __rb_tree_iterator<Value, Value&, Value*> iterator; typedef __rb_tree_iterator<Value,const Value&, const Value*>const_iterator; typedef __rb_tree_iterator<Value, Ref, Ptr> self; typedef __rb_tree_node<Value>* link_type; __rb_tree_iterator() {} __rb_tree_iterator(link_type x) { node = x; } __rb_tree_iterator(const iterator& it) {node = it.node; } reference operator*() const { returnlink_type(node)->value_field; }#ifndef__SGI_STL_NO_ARROW_OPERATOR pointer operator->() const { return &(operator*()); }#endif /*__SGI_STL_NO_ARROW_OPERATOR */ self& operator++() { increment();return *this; } self operator++(int) { self tmp = *this; increment(); return tmp; } self& operator--() { decrement();return *this; } self operator--(int) { self tmp = *this; decrement(); return tmp; }};
以上即为这里关于RB-tree的内容,细致实现之前已经有过,这里就不再重复。
关于Set
Set的特性是所有元素都会根据元素的键值自动被排序。Set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值。Set不允许两个元素具有相同的键值(就是说用的是RB-tree中的insert_unique插入方法)
我们不能通过set的迭代器更改set的元素值,因为set的元素值就是其键值,关系到其排列规则。(通过查看set实现中关于iterator的声明我们可以看出来:
typedef typename rep_type::const_iterator iterator;
)
Set以RB-tree为底层机制,几乎所有的set操作行为,都只是转调用RB-tree的操作
因此,如果希望深入的了解set,那么理解RB-tree是最直观的方法
我们可以调用iterator的基础find函数来寻找我们需要的元素,但是通过内部实现我们可以知道它是通过遍历所有元素来查找的,复杂度为O(n)。所以,如果在用RB-tree为底层实现的数据结构中进行寻找时,我们一般使用该结构自己实现的find函数(即set内部实现的find函数,复杂度为O(logn))
关于Map
Map的特性是,所有元素都会根据元素的键值自动被排序。Map的所有元素都是pair,同时
拥有实值和键值。Map不允许两个元素拥有相同的键值
我们可以通过迭代器修改元素的实值,但不允许修改其键值
Map的底层实现也是RB-tree
- STL源码分析之RB-tree结构简析
- 《STL源码剖析》之RB-tree
- STL RB Tree(红黑树)分析
- C++ STL源码学习(之RB Tree篇)
- STL源码剖析-关联式容器之RB-tree
- STL源码剖析——RB-tree
- 【STL源码剖析读书笔记】【第5章】关联式容器之RB-tree
- STL中的RB-tree
- STL-RB Tree
- STL源码剖析——RB-Tree(红黑树)
- STL源码——RB-Tree insert操作
- STL源码剖析——红黑树(RB-tree)
- WhatWeb源码分析之lib/target.rb
- WhatWeb源码分析之lib/plugins.rb
- SGI-STL学习笔记之RB-tree part1
- SGI-STL学习笔记之RB-tree part2
- STL源码阅读-红黑树(RB-tree)、AVL树、AA树
- STL源码剖析 - 第5章 关联式容器 - RB-tree(红黑树)
- C++字符串转数字,数字转字符串
- MongoDB 索引构建情况分析、MongoDB 安全
- Ubuntu 14.04.2 LTS samba 配置samba服务
- 【AC自动机】【HDOJ2222】 Keywords Search
- SQLite内存使用情况分析
- STL源码分析之RB-tree结构简析
- Python图像处理(9):Hu矩
- 黑马程序员——Java语言基础(一)
- 包装设计模式
- 数组复习
- 我所理解的设计模式(C++实现)——访问者模式(Visitor Pattern)
- 桌面快捷方式上面出现了蓝色的?号
- Android虚拟机学习总结Dalvik虚拟机进程和线程的创建过程分析
- oc 编译