<STL源码剖析>导览
关联式容器
- 所谓关联式容器,观念上类似关联式数据库,每组数据都有一个键值(key)和一个实值(value)。当元素被插入到容器中时,容器内部结构(RB-Tree,或者是hash-table)便依照其键值大小,以某种特定规则将这个元素放置于适当位置。
一般而言,关联式容器的内部结构是一个balanced binary tree(平衡二叉树),以便获得良好的搜寻效率.平衡二叉树有许多种类,包含AVL-tree,RB-tree,AA-tree.
二叉搜索树
- 所谓二叉搜索树(binary search tree),可提供对数时间的元素插入和访问。二叉搜索树的节点放置规则是:任何节点的键值一定大于其左子树中的每一个节点的键值,并小于右子树中的每一个节点的键值。
平衡二叉搜索树(balance binary search tree)
- 在某些情况下,输入值不够随机,或者经过某些插入或删除操作,二叉搜索树可能会失去平衡,造成搜寻效率低下的情况。
- 所谓树形平衡与否并没有一个绝对的测量标准.“平衡”的大致意义是:没有任何一个节点过深。AVL-Tree,RB-Tree均可实现出平衡二叉树,它们**比一般的二叉搜索树复杂。但是它们可以避免极难应付的最坏情况(**高度不平衡)情况,而且由于它们总是保持某种程度的平衡,所以元素的查找时间平均而言也就比较少,一般而言其搜寻时间可节省25%左右。
RB-Tree(红黑树)
- 规则
- 每个节点不是红色就是黑色
- 根节点为黑色
- 如果节点为红,其子节点必须为黑
- 任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须为黑。
插入节点的情况
- 区分内外侧,子节点位于父节点的方位跟父节点位于祖父节点的的方位一致就为外侧,否则为内侧
- 父节点位于祖父节点的左侧,即为左侧
- 父节点位于祖父节点的右侧,即为右侧
- 外侧插入 单旋转
- 左侧 : 右旋
- 右侧 : 左旋
- 内侧插入 双旋转
- 左侧的 先左旋再右旋
- 右侧的 先右旋再左旋
- 可能产生不平衡状态(高度相差1)。rb-tree的平衡性本来就比avl-tree弱,然后rb-tree通常能够导致良好的平衡状态。经验告诉我们,rb-tree的搜寻平均效率和avl-tree几乎相同
一个由上而下的程序
- 假设新增节点为A,那么就延着A的路径,只要看到有某节点X的两个子节点皆为红色,就把X改为红色,并把两个节点改为黑色。
- 但是如果X的父节点P为红色(此时S绝不可能为红) ,就得像状况1一样,做一次单旋转并改变颜色,或者想状况2一样做双旋转,并改变颜色
- 之后插入就很单纯了,要么直接插入,要么插入后再旋转
RB-Tree的节点设计
typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false; 红色为0
const __rb_tree_color_type __rb_tree_black = true; 黑色为1
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
{
typedef __rb_tree_node_base::base_ptr base_ptr; 基类节点指针
typedef bidirectional_iterator_tag iterator_category; 迭代器类型
typedef ptrdiff_t difference_type;
base_ptr node; 基类节点指针 跟容器的联系
重载++操作符调用
void increment()
{
if (node->right != 0) { 如果有右子节点,则需要找右边最小的节点 (状况1)
node = node->right; 就向右走
while (node->left != 0) 然后一直往左走,走到最小,即为右边最小节点
node = node->left; 即是答案
}
else { 如果没有右子节点(状况2)
base_ptr y = node->parent; 找出父节点
while (node == y->right) { 如果节点本身是父节点的右节点
node = y; 则一直往上直到不是右节点
y = y->parent;
}
if (node->right != y) 如果此时的右节点,不等于父节点
node = y; 此时的父节点即是答案 即,node是子树的最有右节点,则找到子树的父节点即为答案 (状况3)
否则此时node即为答案(head)
}
注意,以上判断「若此时的右子节点不等于此时的父节点」,是为了应付特殊情况
特殊情況:我们想寻找根节点的下一个节点,此时根节点无右子节点。
需要联系root节点与header的特殊关系
}
// 重载--操作符调用
void decrement()
{
if (node->color == __rb_tree_red && 如果是红节点
node->parent->parent == node) 父节点的父节点等于自己
node = node->right; 右节点即为答案( 状况1)
以上情況发生于node为header时(即 node 为 end() 时)。
header的右子节点指向整颗树的max
else if (node->left != 0) { 如果有左节点。即左节点的最大值,左节点之后一直往右走(状况2)
base_ptr y = node->left; 令y指向左节点
while (y->right != 0) 当y有右节点时
y = y->right; 一直往右走
node = y; 即为答案
}
else { 即非header节点,也没有左子节点(子树的最左边,最小值)
base_ptr y = node->parent; 找出父节点
while (node == y->left) { 当前节点是父节点的左节点时(子树的最左边,最小值)
node = y; 一直往上走,找到节点部位父节点的左节点时
y = y->parent;
}
node = y; 此时的父节点即为答案
}
}
};
- 迭代器
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 { return link_type(node)->value_field; } 返回元素
重载前++,找下个节点
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的数据结构
- 容器
template <class Key, class Value, class KeyOfValue, class Compare,
class Alloc = alloc>
class rb_tree {
protected:
typedef void* void_pointer;
typedef __rb_tree_node_base* base_ptr; 基类节点指针
typedef __rb_tree_node<Value> rb_tree_node; 子类节点
typedef simple_alloc<rb_tree_node, Alloc> rb_tree_node_allocator; 节点配置器
typedef __rb_tree_color_type color_type; 颜色类型
typedef __rb_tree_iterator<value_type, reference, pointer> iterator; 迭代器
public:
typedef Key key_type;
typedef Value value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef rb_tree_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
// RB-tree 的成员变量
size_type node_count; // 记录节点数量
link_type header; // header节点
Compare key_compare; // 节点间键值大小的比较,仿函数.类的()
}
- 申请节点,释放节点
protected:
link_type get_node() { return rb_tree_node_allocator::allocate(); } 分配一个节点内存
void put_node(link_type p) { rb_tree_node_allocator::deallocate(p); } 释放一个节点内存
link_type create_node(const value_type& x) {
link_type tmp = get_node(); 配置一个节点
__STL_TRY{
construct(&tmp->value_field, x); place new,调用数据类型的构造函数(用x构造)
}
__STL_UNWIND(put_node(tmp));
return tmp;
}
void destroy_node(link_type p) {
destroy(&p->value_field); 释放节点析构元素
put_node(p); 释放节点内存给内存池
}
}
- 容器初始化
三个函数获取header
link_type& root() const { return (link_type&)header->parent; } header的父节点为root
link_type& leftmost() const { return (link_type&)header->left; } header的左节点指向最小值
link_type& rightmost() const { return (link_type&)header->right; } header的右节点指向最大值
void init() {
header = get_node(); 申请一个节点内存
color(header) = __rb_tree_red; 令header为红,用于区分root
root() = 0; header的父节点为空
leftmost() = header; 令 header 的左节点为自己
rightmost() = header; 令 header 的右节点为自己。
}
- 常用容器函数实现
iterator begin() { return leftmost(); } 红黑树的起点为最左点,也就是Key值最小的那个节点
重载了--。所以--end()是,header的右子节点,
也就是红黑树的最右点 ,符合前闭后开原则,end是最后一个节点的下一个
iterator end() { return header; }
- 插入元素(insert_unique)
- 效率 O(lg(N)
- 最多旋转两次
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::
__insert(base_ptr x_, base_ptr y_, const Value& v) {
在x处安插v,父节点为y
link_type x = (link_type)x_;
link_type y = (link_type)y_;
link_type z;
key_compare 是键值大小比较
往左插入
if (y == header || x != 0 || key_compare(KeyOfValue()(v), key(y))) {
z = create_node(v); 产生一个新节点并用v初始化
left(y) = z; 令y的左节点为x 当y为header时,leftmost() = z
if (y == header) {
root() = z; header的父节点为root,z为root
rightmost() = z; header的右节点也指向root
}
else if (y == leftmost()) 如果父节点为最左节点
leftmost() = z; 调整leftmost()(header->left),使它永远指向最小点
}
else { 往右插入
z = create_node(v); 产生一个新节点
right(y) = z; 令新节点成为父节点的右节点
if (y == rightmost()) 如果父节点是最右节点(最大值)
rightmost() = z; 调整rightmost()(header->right),使它永远指向最右节点
}
parent(z) = y; 设定新节点的父节点为z
left(z) = 0; 设定新节点的左子点为空
right(z) = 0; 设定新节点的右子点为空
旋转,跳跃,我闭着眼~
__rb_tree_rebalance(z, header->parent); z为新增节点,header->parent为root节点
++node_count; 节点梳理增加
return iterator(z); 用新增的节点构造一个迭代器变量返回
}
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
pair<typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator, bool>
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_unique(const Value& v)
{
link_type y = header;
link_type x = root(); 从根节点开始
bool comp = true;
while (x != 0) { 从根节点开始找到合适的插入位置
y = x;
comp = key_compare(KeyOfValue()(v), key(x)); v的键值小于当前节点的键值
x = comp ? left(x) : right(x); 小于当前节点的键值则左走,否则右走
}
while循环结束后,y节点即为所安插节点的父节点,必为节点(第一次插入时,header即为父节点)
iterator j = iterator(y); 令j等于即将安插节点的父节点
if (comp) 标识是安插在父节点的左侧还是右侧,comp为true 安插在左侧
if (j == begin()) 如果安插点元素之父节点为最左节点
x 为安插点,y 为安插点之父节点,v为新值。
return pair<iterator, bool>(__insert(x, y, v), true);
else 如果安插点之父节点不是最左节点
--j; 调整安插点之父节点--
if (key_compare(key(j.node), KeyOfValue()(v)))
v的键值大于j.node的键值
如果v本来就是要插在y的右侧那么 为v的键值大于y键值那么为true
如果v本来是要安插在y的左侧,那么一定比--y大,不然父节点不会是y,所以v的键值肯定比--y的键值大,否则是有相同的键值
在x处安插v,父节点为v
return pair<iterator, bool>(__insert(x, y, v), true);
进行至此,标识新值一定与树中键值重复,那么就不该插入新值
return pair<iterator, bool>(j, false);
}
红黑树旋转调整
- 旋转调整
右旋
将x的左子节点的右子节点调整为x
inline void
__rb_tree_rotate_right(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
x为旋转点
__rb_tree_node_base* y = x->left; y为x的左子节点
x->left = y->right; x的左子节点调整为y的右子节点
if (y->right != 0)
y->right->parent = x; y的右子节点的父节点调整为x
y->parent = x->parent; y的父节点调整为x的父节点
if (x == root) x为root
root = y; 调整root
else if (x == x->parent->right) x为父节点的右子节点时
x->parent->right = y; 调整x父节点的右子节点为x的左子节点
else x为父节点的左子节点时
x->parent->left = y; 调整x父节点的左子节点为x的左子节点
y->right = x; y的右节点为x
x->parent = y; x的父节点为y
左旋,把x的右子节点的左子节点调整为x
inline void
__rb_tree_rotate_left(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
x为旋转点
__rb_tree_node_base* y = x->right; y为x的右节点
x->right = y->left; y的Left,右边的小边,给到x的右节点
if (y->left != 0)
y->left->parent = x; y的left的父节点赋值为x
y->parent = x->parent;
if (x == root) x为根节点时
root = y; 将右节点设置为root节点
else if (x == x->parent->left) 如果x为其父节点的左节点
x->parent->left = y; 调整x的父节点的左节点为x的右节点
else 如果x为其父节点的右节点
x->parent->right = y; 调整x的父节点的右节点为x的右节点
y->left = x; y的左节点为x
x->parent = y; x的父节点为y
}
- 自上而下的调整
重新令树形平衡,改变颜色旋转树形
参数一为新增节点,参数2为root根节点
全局函数
while循环最多执行两次
inline void
__rb_tree_rebalance(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
x->color = __rb_tree_red; 新节点必为红
while (x != root && x->parent->color == __rb_tree_red) { 父节点为红,且父节点不是根节点
if (x->parent == x->parent->parent->left) { 父节点为祖父节点的左子节点
__rb_tree_node_base* y = x->parent->parent->right; y为伯父节点
if (y && y->color == __rb_tree_red) { 伯父节点存在且为红
自上而下的设计原则,两个子树都为红色是调整为黑色
x->parent->color = __rb_tree_black; 更改父节点为黑
y->color = __rb_tree_black; 更改伯父节点为黑
x->parent->parent->color = __rb_tree_red; 更改祖父节点为红
x = x->parent->parent; 检查祖父节点的情况,是否需要调整
}
else { 无伯父节点或伯父节点为黑
if (x == x->parent->right) {
父节点为祖父节点的左子节点时,新节点为父节点的右节点
内侧插入双旋转,先左旋再右旋
x = x->parent;
__rb_tree_rotate_left(x, root); 左旋,第一参数为左选点
}
状况一的右旋(外侧插入)
x->parent->color = __rb_tree_black; 改变父节点为黑
x->parent->parent->color = __rb_tree_red; 改变祖父节点为红(伯父节点为黑不需要考虑)
__rb_tree_rotate_right(x->parent->parent, root); 右旋改变父节点
}
}
else { 父节点为祖父节点的右子节点
__rb_tree_node_base* y = x->parent->parent->left; y为伯父节点
第二次while循环时,此时的伯父节点一定为黑
if (y && y->color == __rb_tree_red) { 如果伯父节点为红
根据自上而下的设计原则
x->parent->color = __rb_tree_black; 更改父节点为黑色
y->color = __rb_tree_black; 更改伯父节点为黑色
x->parent->parent->color = __rb_tree_red; 更改祖父节点为红
x = x->parent->parent; 检查祖父节点的情况
}
else {
if (x == x->parent->left) {
父节点为祖父节点的右子节点时,且是插入父节点的左侧
内侧插入,双旋转,先右旋再左旋
x = x->parent;
__rb_tree_rotate_right(x, root);
}
外侧插入
x->parent->color = __rb_tree_black; 改变父节点为黑
x->parent->parent->color = __rb_tree_red; 改变祖父节点为红(伯父节点为黑不需要考虑)
__rb_tree_rotate_left(x->parent->parent, root); 左旋
}
}
} // while 结束
root->color = __rb_tree_black; 根节点永远为黑
}
红黑树的删除
二叉搜索树的删除<算法导论>
- 从一棵二叉搜索树T中删除一个节点z的整个策略分为三种基本情况
- 如果 z 没有孩子结点,那么只是简单地将它删除,并修改它的父节点
- 如果 z 只有一个孩子,那么将这个孩子提升到树中的 z 的位置上,并修改z的父节点,用x的孩子来替换z
- 如果 z 有两个孩子,那么找z 的后继y(一定在 z 的右子树中),并让y占据树中z 的位置。z 的原来的右子树部分成为y的新的右子树,并且 z 的左子树成为y的新的左子树
- 这里其实有两种策略,找左边最大的或者右边最小的节点,替代 z
rb-tree的删除
脑瓜子嗡嗡的吧哈哈哈哈
红黑树的删除策略
- O(lgN)
- 删除至多旋转三次
- 示意图<来自算法导论>
- 找兄弟节点借红节点,变黑节点,填充删除的黑节点
- 找兄弟节点借红节点,变黑节点,填充删除的黑节点
- 源码
- 删除调整
inline __rb_tree_node_base*
__rb_tree_rebalance_for_erase(__rb_tree_node_base* z,
__rb_tree_node_base*& root,
__rb_tree_node_base*& leftmost,
__rb_tree_node_base*& rightmost)
{
__rb_tree_node_base* y = z;
__rb_tree_node_base* x = 0;
__rb_tree_node_base* x_parent = 0;
if (y->left == 0) 无左子节点
x = y->right; x为y的右子节点
else y有左子节点
if (y->right == 0) y右子节点为空
x = y->left; x为y的左子节点(不为空)
else { 如果y既有左子节点,又有右子节点,设置y为z的替代者,x可能为空
y = y->right;
while (y->left != 0)
y = y->left;
x = y->right; x为y(替代者)的右子节点(阔能为空)
}
y是z的替代者
if (y != z) {
z->left->parent = y; 调整z的左子节点的父节点为y
y->left = z->left; 赋值 y的左子节点 = z的左子节点
if (y != z->right) { y不是z的右子节点
x_parent = y->parent; 记录y的parent
if (x) x->parent = y->parent; 如果x存在(需要调整x的父节点为y的父节点),也就是x替代y
y->parent->left = x; y的parent的左子节点为x
y->right = z->right; y替代z,y的右子节点指向z的右子节点
z->right->parent = y; z的右子节点的父节点置为y
}
else
x_parent = y;
if (root == z) 如果删除的是为根节点
root = y; 更新根节点
else if (z->parent->left == z) 调整z的父节点
z->parent->left = y; 如果z是其父节点的左子节点,那么父节点的左子节点指向y
else
z->parent->right = y; 否则z的父节点的右子节点指向y
y->parent = z->parent; 设置y的parent为z的parent
__STD::swap(y->color, z->color); 将z的颜色与y的颜色交换
y = z; y此时指向要删除的节点
}
else { 如果y == z 也就是没有右节点 此时x为y的left节点
x_parent = y->parent; 记录替换节点y的parent
if (x) x->parent = y->parent; 要删除y,所以x的的父节点指向y的父节点
if (root == z)
root = x; 调整根节点
else
if (z->parent->left == z) 调整父节点
z->parent->left = x; 如果z是其父节点的左子节点,则其父节点的左子节点指向x
else
z->parent->right = x; 否则z的父节点的右子节点指向x
if (leftmost == z) 如果z是最小值
if (z->right == 0) 如果z没有右子节点
leftmost = z->parent; 调整最小值
else 如果z == root leftmost = header
leftmost = __rb_tree_node_base::minimum(x);
if (rightmost == z)
if (z->left == 0)
rightmost = z->parent; 如果z是最大值,调整最大值为z的parent
else 当z == root时,调整rightmost为header
rightmost = __rb_tree_node_base::maximum(x);
}
if (y->color != __rb_tree_red) { 当替换节点为黑时
x不为空时,替代了y原来的位置
while (x != root && (x == 0 || x->color == __rb_tree_black))
if (x == x_parent->left) { x为左子节点
__rb_tree_node_base* w = x_parent->right; 兄弟节点
if (w->color == __rb_tree_red) { 如果兄弟节点为红(示意图的情况一)
w->color = __rb_tree_black; 兄弟节点置为黑
x_parent->color = __rb_tree_red; 父节点置为红
__rb_tree_rotate_left(x_parent, root); 左旋
w = x_parent->right; (左旋时兄弟节点可能会变)调整兄弟节点
}
if ((w->left == 0 || w->left->color == __rb_tree_black) && 兄弟节点的两个子节点都为黑
(w->right == 0 || w->right->color == __rb_tree_black)) {
w->color = __rb_tree_red; 调整兄弟节点为红 (示意图的情况二)
x想上调整
x的parent向上调整
(自下而上调整,找父节点的兄弟节点调整(找兄弟节点借红色变黑色,填补删掉的黑色))
x = x_parent;
x_parent = x_parent->parent;
}
else {
if (w->right == 0 || w->right->color == __rb_tree_black) { 兄弟节点的右子节点为黑,左子节点为红
if (w->left) w->left->color = __rb_tree_black; 调整兄弟节点的左子节点为黑(示意图的情况三)
w->color = __rb_tree_red; 调整兄弟节点为红
__rb_tree_rotate_right(w, root); 右旋
w = x_parent->right; (右旋导致兄弟节点更新) 调整兄弟节点
}
w->color = x_parent->color; 调整兄弟节点的颜色为父节点颜色
x_parent->color = __rb_tree_black; 调整父节点的颜色为黑
if (w->right) w->right->color = __rb_tree_black; 如果父节点的右子节点存在,调整为黑色
__rb_tree_rotate_left(x_parent, root); 左旋 (示意图情况四)
break;
}
}
else { 对称操作
__rb_tree_node_base* w = x_parent->left;
if (w->color == __rb_tree_red) {
w->color = __rb_tree_black;
x_parent->color = __rb_tree_red;
__rb_tree_rotate_right(x_parent, root);
w = x_parent->left;
}
if ((w->right == 0 || w->right->color == __rb_tree_black) &&
(w->left == 0 || w->left->color == __rb_tree_black)) {
w->color = __rb_tree_red;
x = x_parent;
x_parent = x_parent->parent;
}
else {
if (w->left == 0 || w->left->color == __rb_tree_black) {
if (w->right) w->right->color = __rb_tree_black;
w->color = __rb_tree_red;
__rb_tree_rotate_left(w, root);
w = x_parent->left;
}
w->color = x_parent->color;
x_parent->color = __rb_tree_black;
if (w->left) w->left->color = __rb_tree_black;
__rb_tree_rotate_right(x_parent, root);
break;
}
}
if (x) x->color = __rb_tree_black; 置为黑色
}
return y; 返回要删除的节点
}
- 删除
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
inline void
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::erase(iterator position) {
link_type y = (link_type)__rb_tree_rebalance_for_erase(position.node,
header->parent,
header->left,
header->right);
destroy_node(y); 析构,释放内存
--node_count; 节点数量减少
}