目录
1 关联式容器
所谓关联式容器,观念上类似关联式数据库(实际上则简单许多):每笔数据(每个元素)都有一个键值(key)和一个实值(value)。当元素被插入到关联式容器中时,容器内部结构(可能是RB-tree,也可能是hash-table)便依照其键值大小,以某种特定规则将这个元素放置于适当位置。关联式容器没有所谓头尾(只有最大元素和最小元素),所以不会有所谓 push_back()、push_front()、pop_back()、pop_front()、begin()、end()这样的操作行为。
2 树
2.1 二叉树
2.2 二叉搜索树
可提供对数时间的元素插入和访问。
节点放置规则:任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树中的每一个节点的键值
节点插入:
节点删除:
2.3 平衡二叉搜索树
2.4 AVL tree
2.4.1 定义
“加上了额外平衡条件”的二叉搜索树,要求任何节点的左右子树高度相差最多1
平衡被破坏的四种情况:
2.4.2 单旋转
2.4.3 双旋转
3 RB-tree(红黑树)
3.1 特性
不仅是一个二叉搜索树(相对接近平衡),还需满足以下条件:
- 每个节点不是红色就是黑色
- 根节点为黑色
- 每个叶结点是黑色(此处的叶节点是指为空NULL的叶子节点)
- 父子两节点不得同时为红
- 任意节点至NULL(树尾端)的任何路径,所含之黑节点数目必须相同
3.2 插入节点
插入新节点破坏RB-tree的规则,必须调整树形,即旋转树形并改变节点颜色
3.3 一个由上而下的程序
3.4 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; //节点值
};
3.5 RB-tree的迭代器
RB-tree的迭代器属于双向迭代器,但不具备随机定位能力。RB-tree迭代器的前进操作operator++()
调用了基层迭代器的increment()
,RB-tree迭代器的后退操作operator--()
则调用了基层迭代器的decrement()
。
//基层迭代器
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) { //如果有右子节点
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;
}
if(node->right != y) node = y; //右子节点不等于此时的父节点
}
}
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;
}
}
};
//正规迭代器
template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public _rb_tree_base_iterator {
//...
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;
}
};
3.6 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;
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:
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); //构造内容
}
__STL_UNWIND(put_noed(tmp));
return tmp;
}
void destory_node(link_type p) {
destory(&p->value_field); //析构内容
put_node(p); //释放内存
}
维护RB-tree的三笔数据:
protected:
size_type node_count; //记录树的大小(节点数量)
link_type header; //实现上的一个技巧
Compare key_compare; //节点间的键值大小比较准则
构造与析构:
private:
void init() {
header = get_node(); //产生一个节点空间,令header指向它
color(header) = _rb_tree_red;
root() = 0;
leftmost() = header; //令header的左子节点为自己
rightmost() = header; //令header的右子节点为自己
}
public:
rb_tree(const Compare& comp = Compare()) : node_conut(0), key_compare(comp) {
init();
}
~rb_tree() {
clear();
put_node(header);
}
3.7 RB-tree的构造与内存管理
由上述RB-tree的数据结构可知,它定义了专属的空间配置器**rb_tree_node_allocator
**。
RB-tree的构造方式有两种,一种是以现有的RB-tree复制一个新的RB-tree,另一种是产生一颗空树:
rb_tree<int, int, identity<int>, less<int>> itree;
上述代码指定了节点的键值、实值、大小比较标准然后调用了RB-tree的默认构造函数,由上一节默认构造的程序可知它调用了init()。
在init()中为RB-tree的根节点再设计了一个父节点,名为header:
因此,再插入新节点时,不仅需要依照RB-tree的规则来调整,还需要维护header的正确性,使其父节点指向根节点,左子节点指向最小节点,右子节点指向最大节点。
3.8 RB-tree的元素操作
3.8.1 元素插入
insert_equal(const Value& v)
:(插入新值,节点键值允许重复)该函数负责从根节点开始,往下寻找适当的插入点,返回__insert(x, y, v)
真正负责元素插入insert_unique(const Value& v)
:(插入新值,节点键值不允许重复,若重复则插入无效),返回值是一个pair,第一元素是RB-tree迭代器,指向新增节点,第二元素表示插入成功与否
真正的插入执行程序:
__insert(base_ptr x_, base_ptr y_, const Value& v)
x_
为新值插入点,y_
为插入点之父节点,v为新值
在该程序中维护header的正确性,并调用__rb_tree_rebalance(z, header->parent)
调整RB-tree。
调整RB-tree(旋转及改变颜色):
__rb_tree_rebalance(__rb_tree_node_base* x, __rb_tree_node_base*& root)
程序实现逻辑可参考上述3.2、3.3节,根据不同情况有时只需调整节点颜色,有时要单旋转(右旋或左旋),有时要双旋转
左旋:__rb_tree_rotate_left(__rb_tree_node_base* x, __rb_tree_node_base*& root)
右旋:__rb_tree_rotate_right(__rb_tree_node_base* x, __rb_tree_node_base*& root)
3.8.2 元素的搜寻
RB-tree提供的find函数:
//寻找RB数中是否有键值为k的节点
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>::find(const Key& k) {
link_type y = header;
link_type x = root();
while(x != 0) {
if(!key_compare(key(x), k))
//x键值大于k,遇到大值往左走
y = x, x = left(x);
else
//x键值小于k,遇到小值往右走
x = right(x);
iterator j = iterator(y);
return (j == end() || key_compare(k, key(j.node))) ? end() : j;
}
}