STL源码剖析:关联式容器

AVL树

  • AVL树定义:红黑树是一颗二叉搜索树,特别的是一棵保持高度平衡的二叉搜索树

  • AVL树特点:

    • 每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1

  • AVL树插入:

    • 说明:新增节点的平衡因子是0,新增节点是右节点,父节点平衡因子+1,新增节点是左节点,父节点平衡因子-1

    • 插入新增节点后,父节点平衡因子变为0,说明节点插入在较矮的子树上,平衡没被破坏,高度也没变化,直接插入无需做任何处理

    • 插入新增节点后,父节点平衡因子变为+1或是-1,说明插入前节点的平衡因子是0,平衡没被破坏,但是高度+1.需要向上调整

    • 插入新增节点后,父节点平衡因子变为+2或是-1,说明节点插入在较高的子树上,平衡被破坏,根据下面4中情况调整

      • LL型:右旋

      • LR型:左旋变成LL型,右旋

      • RR:左旋

      • RL:右旋变成RR型,左旋

      • 调整后恢复平衡,且高度没有变化

  • AVL树删除:

    • 删除节点有两个孩子节点:中序遍历,找出删除节点的前驱或是后继节点,交换二者的数据,然后删除节点变成下面两种情况中的一种

    • 删除节点只有一个孩子节点:孩子节点替代删除的节点,向上调整

    • 删除节点无孩子节点:直接删除目标节点,向上调整

    • 向上调整:

      • 删除后平衡因子不变,不做处理

      • 删除左子树的节点,若失去平衡,令t=右子树,若t的左子树高度>t的右子树高度,相当于在右子树的左子树插入节点,执行RL操作,否则执行RR操作

      • 删除右子树的节点,若失去平衡,令t=左子树,若t的左子树高度>t的右子树高度,相当于在左子树的左子树插入节点,执行LL操作,否则执行LR操作

红黑树

  • 红黑树定义:红黑树是一颗二叉搜索树,特别的是一棵保持一定平衡的二叉搜索树

  • 红黑树的特点:

    1. 节点是红色或黑色

    2. 根节点是黑色

    3. 每个叶节点(NULL节点,无实际意义,只有颜色属性)是黑色

    4. 每个红色节点的两个子节点都是黑色(即:不能有两个连续的红色节点)

    5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

  • 红黑树的5点性质保证了整颗树中最长路径不大于最短路径的两倍,从而使得整棵树基本保持平衡

  • 红黑数的插入:

    • 插入节点标记为红色(如果是黑的,就会破坏性质5)

    • 插入节点的父节点为黑色,直接插入就行

    • 插入节点的父节点为红色:

      • 插入节点的叔父节点为红色,将插入节点和叔父节点变为黑色,父节点变为红色,向上调整只到根节点

      • 插入节点的叔父节点为黑色:

        • LL型:右旋,变色(不需要向上调整)

        • RL型:右旋变成LL型

        • RR型:左旋,变色(不需要向上调整)

        • LR型:左旋变成RR型

  • 红黑树删除:

    • 删除节点有两个孩子节点:中序遍历,找出删除节点的前驱或是后继节点,交换二者的数据,不交换颜色属性,然后删除节点变成下面两种情况中的一种

    • 删除节点只有一个孩子节点:

      • 删除节点是红色节点:直接删除

      • 删除节点是黑色节点:

        • 孩子节点是红色节点:删除节点后,将孩子节点变黑

        • 孩子节点是黑色节点:不存在这种情况,违反性质5

    • 删除节点无孩子节点

      • 删除节点是红色:直接删除

      • 删除节点是黑色:

        • 删除节点是左孩子

          • 兄弟节点是红色:父节点和兄弟节点的颜色互换,然后左旋,变成兄弟节点是黑色的情况

          • 兄弟节点是黑色:

            • 远侄节点是红色(近侄颜色没要求):父节点和兄弟节点的颜色互换,然后左旋,最后把操作前的远侄节点变成黑色,删除掉需要目标节点即可

            • 远侄节点是黑色:

              • 近侄节点为黑色:

                • 父节点为红色:父亲节点改成黑色,将兄弟节点改成红色,然后删目标节点

                • 父节点为黑色:将兄弟节点S的颜色改成红色,删除目标节点,以父节点为起点,向上调整

              • 近侄节点为红色:右旋,交换兄弟节点和近侄节点的颜色,变成远侄为红色节点的情况

        • 删除节点是右孩子

          • 兄弟节点是红色:父节点和兄弟节点的颜色互换,然后左旋,变成兄弟节点是黑色的情况

          • 兄弟节点是黑色:

            • 远侄节点是红色:父节点和兄弟节点的颜色互换,然后左旋,最后把操作前的远侄节点变成黑色,删除掉需要目标节点即可

            • 远侄节点是黑色:

              • 近侄节点为黑色:

                • 父节点为红色:父亲节点改成黑色,将兄弟节点改成红色,然后删目标节点

                • 父节点为黑色:将兄弟节点S的颜色改成红色,删除目标节点,以父节点为起点,向上调整

              • 近侄节点为红色:左旋,交换兄弟节点和近侄节点的颜色,变成远侄为红色节点的情况

  • 红黑树删除的口诀:先看待删除的节点的颜色,再看兄弟节点的颜色,再看侄子节点的颜色(侄子节点先看远侄子再看近侄子),最后看父亲节点的颜色

RB-tree和iterator之间的关系

RB-tree节点设计

typedef 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; // 指向父节点
    bsae_ptr left; // 指向左孩子
    base_ptr right; // 指向右孩子
    
    static base_ptr mimimum(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)
        {
            node = node->right;
            while(node->left != 0)
            {
                node = node->left;
            }
        }
        else
        {
            // 当前节点的右节点不存在,就寻找第一个父节点且父节点的右孩子不是自己的节点,就是下一个节点
            base_ptr y = node->parent;
            while(node == y->right)
            {
                node = y;
                y = node->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)
        {
            // 当前节点存在左节点,寻找左节点中的最大值(一直向右走,走到底就是最大值)
            node = node->left;
            while(node->right != 0)
            {
                node = node->right;
            }
        }
        else
        {
            // 当前节点的左节点不存在,就寻找第一个父节点且父节点的左孩子不是自己的节点,就是上一个节点
            base_ptr y = node->parent;
            while(node == y->left)
            {
                node = y;
                y = node->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;
    }
    
    pointer operator->() const
    {
        return &(operaotr*());
    }
    
    self& operator++()
    {
        increment();
        return *this;
    }
    
    self operator++(int)
    {
        self temp = *this;
        increment();
        return temp;
    }
    
    self& operator--()
    {
        decrement();
        return *this;
    }
    
    self operator--(int)
    {
        self temp = *this;
        decrement();
        return temp;
    }
}

RB-tree数据结构

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc = alloc>
class rb_tree
{
protect:
    typedef void* void_pointer;
    typedef __rb_tree_node_base* base_ptr;
    typedef __rb_tree_node<Value> tr_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 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;
    
protect:
    link_type get_node()
    {
        return rb_tree_node_allocator::allocate();
    }
    
    void put_node(link_type p)
    {
        rb_tree_node_allocator::dealocate(p);
    }
    
    link_type create_node(const value_type& x)
    {
        link_type tmp = get_node();
        construct(&tmp->value_field, x);
        return tmp;
    }
    
    void destroy_node(link_type p)
    {
        destroy(&p->value_field)
        put_node(p)   
    }
    
protected:
    size_type node_count; // 记录节点总个数
    link_type header; // 门卫节点
    Compare key_compare; // 比较key值大小的函数
    
    link_type& root() const { return (link_type&)header->parent; }
    link_type& leftmost() const { return (link_type&)header->left; }
    link_type& rightmost() const { return (link_type&)header->right; }
    
    static link_type minimun(link_type x)
    {
        return (link_type) __rb_tree_node_base::minimun(x);
    }
    
    static link_type maximun(link_type x)
    {
        return (link_type) __rb_tree_node_base::maximun(x);
    }
    
public:
    typedef __rb_tree_iterator<value_type, reference, pointer> iterator;

private:
    void init()
    {
        header = get_node();
        color(header) = __rb_tree_red;
        
        // 初始化时都指向自己
        root() = 0;
        leftmost() = header;
        rightmost() = header;
    }
    
public:
    rb_tree(const Compare& comp = Compare()) : node_count(0), key_compare(comp)
    {
        init();
    }
    
    // 不可插入相同的key,否则失败
    pair<iterator, bool> insert_unique(const value_type& x);
    
    // 可以插入相同的key
    pair<iterator, bool> insert_equal(const value_type& x);
    
    // 查找
    iterator find(const Key& k)
    {
        link_type y = header;
        link_type x = root();
        
        while(x != 0)
        {
            if(!kwy_compare(key(x), k))
            {
                y = x;
                x = left(x);
            }
            else
            {
                x = right(x);
            }
        }
        
        iterator j = iterator(y);
        
        return (j == end() || key_compare(k, key(j.node))) ? end() : j;
    }
}

set

  • 底层是红黑树

  • set中的元素只有key,没有value

  • set中不允许存在两个相同的元素

multiset

  • mutliset和set基本类似,唯一的不同是set不允许重复,mutliset允许重复

  • set使用insert_unique,mutliset使用insert_equal

map

  • 底层是红黑树

  • map中的元素是键值对

  • map中不允许存在两个相同的元素

  • 键值对pair的定义:

template <class T1, class T2>
struct pair
{
    typedef T1 first_type;
    typedef T2 second_type;
    
    T1 first;
    T2 second;
    
    pair() : first(T1()), seocond(T2()) {}
    pair(const T1& a, const T2& b) : first(a), second(b) {}
}

multimap

  • mutlimap和map基本类似,唯一的不同是map不允许key重复,mutlimap允许重复

  • map使用insert_unique,mutlimap使用insert_equal

hashtable

  • 以空间换时间

  • 用一个足够大的vector保存所有的数据,为了使得所有数据都可以对应数组中唯一的一个下标,因此使用一个映射函数,将数据映射成下标,然后存储到vector中

    • 如果映射的下标处已经存在数据,就发生“碰撞”,需要使用其他方法进行规避,如:

      • 线性探测

      • 二次探测

      • 开链

    • 如果映射的下标处没有数据,直接存储

  • hashtable中的数据除以vector的大小叫负载系数,如果负载系数超过一定值,就需要扩大vector

  • 线性探测:如果映射出的下标已经存储了数据,那么将下标加1,进行存储,如果还是已经存储了数据,那么再加1,依次往后...

  • 二次探测:如果映射出的下标已经存储了数据,那么将下标加1^2,进行存储,如果还是已经存储了数据,那么再加2^2,依次往后...

  • 开链:如果映射出的下标已经存储了数据,那么直接将数据头插到该下标指向的链表中,即同一链表中的下标值都相同(类似deque的底层数据存储结构)

hashtable的各种结构定义

  • 节点定义

template <class Value>
struct __hashtable_node
{
    __hashtable_node* next;
    Value val;
};
  • 迭代器定义
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator
{
    typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> hashtable;
    typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;
    typedef __hashtable_node<Value> node;
    
    typedef forward_iterator_tag iterator_category;
    typedef Value value_type;
    typedef ptrdiff_t difference_type;
    typedef size_t size_type;
    typedef value& reference;
    typedef value* pointer;

    node* cur;
    hashtable* ht;
    
    __hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab){}
    __hashtable_iterator() {}
    
    reference operator*() const { return cur->val; }
    pointer operator->() const { return &(operator*()); }
    
    iterator& operator++()
    {
        const node* old = cur;
        cur = cur->next;
        if(!cur)
        {
            size_type bucket = ht->bkt_num(old->val);
            while(!cur && ++bucket < ht->buckets.size())
            {
                cur = ht->buckets[bucket];
            }
        }
        return *this;
    }
    
    iterator& operator++(int)
    {
        iterator tmp = *this;
        ++*this;
        return tmp;
    }
    
    bool operator==(const iterator& it) const { return cur == it.cur; }
    bool operator!=(const iterator& it) const { return cur != it.cur; }
}
  • hashtable定义
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
class hashtable
{
public:
    typedef HashFcn hasher;
    typedef EqualKey key_equal;
    typedef size_t size_type;

private:
    hasher hash;
    key_equal equals;
    ExtractKey get_key;
    
    typedef __hashtable_node<Value> node;
    typedef simple_alloc<node, Alloc> node_allocator;
    
    vector<node*, Alloc> buckets;
    size_type num_elements;
    
public:
    size_type bucket_count() const { return buckets.size(); }
}
/*
    Value:节点值类型
    Key:节点的键值类型
    HashFcn:仿函数,计算hash值
    ExtractKey:仿函数,提取key值
    EqualKey:仿函数,判别键值是否相同的方法
    Alloc:空间配置器
*/
node* new_node(const value_type& obj)
{
    node* n = node_allocator::allocate();
    n->next = 0;
    construct(&n->val, obj);
    return n;
}

void delete_node(node* n)
{
    destroy(&n->val);
    node_allocator::deallocate(n);
}

hashtable(size_type n, const HashFcn hf, const EqualKey eql) : hash(hf), equals(eql), get_key(ExtractKey()), num_elements(0) 
{
    initialize_buckets(n);
}

void initialize_buckets(size_type n)
{
    const size_type n_buckets = next_size(n);
    buckets.reserve(n_buckets);
    buckets.insert(buckets.end(), n_buckets, (node*)0);
    num_elements = 0;
}

size_type next_size(size_type n) const
{
      // 有一个大小为28的数组,里面全部是质数。下面函数是取出数组里面最接近n的质数。
    return __stl_next_prime(n);
}
// 插入数据不允许重复
pair<iterator, bool>insert_unique(const value_type& obj)
{
    resize(num_elements + 1); // 判断表格vector是否需要重建,如果需要就重建
    return insert_unique_noresize(obj); // 插入键值,不允许重复
}

void resize(size_type num_elements_hint)
{
    const size_type old_n = buckets.size();
    if(num_elements_hint > old_n)
    {
        vector<node*, Alloc> tmp(n, (node*)0);
        for(size_type bucket = 0; bucket < old_n; ++bucket)
        {
            node* first = buckets[bucket];
            /* 感觉没有必要遍历链表,直接移动链表头结点就行 */
            while(first)
            {
                size_type new_bucket = bkt_num(first->val, n);
                buckets[bucket] = first->next;
                first->next = tmp[new_bucket];
                tmp[new_bucket] = first;
                first = buckets[bucket];
            }
        }
        buckets.swap(tmp);
    }
}

pair<iterator, bool> insert_unique_noresize(const value_type& obj)
{
    const size_type n = bke_num(obj);
    node* first = buckets[n];
    
    for(node* cur = first; cur; cur = cur->next)
    {
        if(equals(get_key(cur->val), get_key(obj)))
        {
            return pair<iterator, bool>(iterator(cur, this), false);
        }
    }
    
    node* tmp = new_node(obj);
    tmp->next = first;
    bucklet[n] = tmp;
    ++num_elements;
    return pair<iterator, bool>(iterator(tmp, this), true);
}
// 插入数据允许重复
iterator insert_equal(const value_type& obj)
{
    resize(num_elements + 1); // 判断表格vector是否需要重建,如果需要就重建
    return insert_equal_noresize(obj); // 插入键值,允许重复
}

iterator insert_equal_noresize(const value_type& obj)
{
    const size_type n = bke_num(obj);
    node* first = buckets[n];
    
    for(node* cur = first; cur; cur = cur->next)
    {
        if(equals(get_key(cur->val), get_key(obj)))
        {
            // 如果有key相同的节点,将新节点插入在相同节点的后面
            node* tmp = new_node(obj);
            tmp->next = cur->next;
            cur->next = tmp;
            ++num_elements;
            return iterator(tmp, this);
        }
    }
    
    node* tmp = new_node(obj);
    tmp->next = first;
    bucklet[n] = tmp;
    ++num_elements;
    return iterator(tmp, this);
}
// 映射函数
size_type bkt_num(const value_type& obj, size_t n) const
{
    return bkt_num_key(get_key(obj), n);
}

size_type bkt_num(const value_type& obj) const
{
    return bkt_num_key(get_key(obj)); 
}

size_type bkt_num_key(const key_type& key) const
{
    return bkt_num_key(get_key(obj), buckets.size());
}

// 最底层干活的函数
size_type bkt_num_key(const key_type& key, size_t n) const
{
    // STL为所有的基础类型都定义了hash函数
    return hash(key) % n;
}

hash_set

  • 使用方式和set完全相同

  • 底层使用hashtable

  • hashset中的元素只有key,没有value

  • hashset中不允许存在key相同的数据

hash_mutliset

  • 和hash_set完全相同,唯一的不同是允许存在相同key的数据

  • 底层使用hashtable

hash_map

  • 使用方式和map完全相同

  • 底层使用hashtable

  • map中不允许存在两个相同的元素

  • 存储的是键值对

hash_mutlimap

  • 和hash_map完全相同,唯一的不同是允许存在相同key 的数据

  • 底层使用hashtable

自定义的类作为map和hash_map的key需要注意的几点

  • 自定义类做map的key

    • 必须从重载operator<

  • 自定义类做hash_map的key

    • 提供equals()

    • 提供hashcode()

posted @ 2019-09-23 19:30  Yong_无止境  阅读(145)  评论(0编辑  收藏  举报