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操作
红黑树
红黑树定义:红黑树是一颗二叉搜索树,特别的是一棵保持一定平衡的二叉搜索树
红黑树的特点:
节点是红色或黑色
根节点是黑色
每个叶节点(NULL节点,无实际意义,只有颜色属性)是黑色
每个红色节点的两个子节点都是黑色(即:不能有两个连续的红色节点)
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
红黑树的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 != )
{
x = x->left;
}
return x;
} static base_ptr maximum(base_ptr x)
{
while(x->right != )
{
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 != )
{
node = node->right;
while(node->left != )
{
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 != )
{
// 当前节点存在左节点,寻找左节点中的最大值(一直向右走,走到底就是最大值)
node = node->left;
while(node->right != )
{
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() = ;
leftmost() = header;
rightmost() = header;
} public:
rb_tree(const Compare& comp = Compare()) : node_count(), 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 != )
{
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 = ;
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()
{
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*));
num_elements = ;
} 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 + ); // 判断表格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*));
for(size_type bucket = ; 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 + ); // 判断表格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()
STL源码剖析:关联式容器的更多相关文章
- STL源码剖析——序列式容器#1 Vector
在学完了Allocator.Iterator和Traits编程之后,我们终于可以进入STL的容器内部一探究竟了.STL的容器分为序列式容器和关联式容器,何为序列式容器呢?就是容器内的元素是可序的,但未 ...
- STL源码剖析——序列式容器#4 Stack & Queue
Stack stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口,元素的新增.删除.最顶端访问都在该出口进行,没有其他位置和方法可以存取stack的元素. ...
- STL源码剖析——序列式容器#2 List
list就是链表的实现,链表是什么,我就不再解释了.list的好处就是每次插入或删除一个元素,都是常数的时空复杂度.但遍历或访问就需要O(n)的时间. List本身其实不难理解,难点在于某些功能函数的 ...
- STL源码剖析——序列式容器#5 heap
准确来讲,heap并不属于STL容器,但它是其中一个容器priority queue必不可少的一部分.顾名思义,priority queue就是优先级队列,允许用户以任何次序将任何元素加入容器内,但取 ...
- STL源码剖析——序列式容器#3 Deque
Deque是一种双向开口的连续线性空间.所谓的双向开口,就是能在头尾两端分别做元素的插入和删除,而且是在常数的时间内完成.虽然Vector也可以在首端进行元素的插入和删除(利用insert和erase ...
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
- STL源码剖析 - RB-tree
在我看来,看源码是一件既痛苦又兴奋的事.当我们在推敲其中的难点时,是及其痛苦的,但当发现实现代码是那么丝滑简洁时,“wc, nb!”. 1. 导语 如果我们去看关联式容器map.set.multima ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- 【转载】STL"源码"剖析-重点知识总结
原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
随机推荐
- 搜索引擎ElasticSearch入门
前言 最近项目上需要用到搜索引擎,由于之前自己没有了解过,所以整理了一下搜索引擎的相关概念知识. 正文 想查数据就免不了搜索,搜索就离不开搜索引擎,百度.谷歌都是一个非常庞大复杂的搜索引擎,他们几乎索 ...
- SpringMVC 学习笔记(六)拦截器
5.1.处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器) 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. ...
- 几个超级实用但很少人知道的 VS 技巧
大家好,今天分享几个我知道的实用 VS 技巧,而这些技巧我发现很多人都不知道.因为我经常在工作中遇到:我在同事电脑上解决问题,或在会议上演示代码示例时,使用了一些 VS "骚"操作 ...
- Mybatis学习笔记(1)
CRUD操作 1.从实体类参数中取值 #{属性名} select * from user where username = #{username} 2.当sql语句只有一个参数且参数类型是基本类型或基 ...
- 入门大数据---Scala学习
Scala是什么? Scala是一种基于函数式编程和面向对象的高级语言.它开发了Spark等大型应用.它和Java有效集成,底层也是支持JVM的. 它有六大特性: 无缝JAVA互操作 Scala在JV ...
- vue全家桶(2.7)
3.11.1.vue-router中的全局钩子函数 在vue-router中,路由发生变化,我们可以做一些事情,例如:可以决定是否进入导航,可以决定跳转到哪里,官方文档中又叫做导航守卫 首先来看一个全 ...
- 一个ioc例子jdk和spring版本导致问题
今天橘子松在做一个简单例子的时候,出现bug让我久久找了半小时... 天啊 不会吧 错误如下: java.lang.NoSuchMethodError: org.springframework.a ...
- css3动画添加间隔
因项目需要,需要在元素上实现动画效果,并且需要有动画间隔.坑爹的是animation-delay只有在第一次动画开始的时候才起效. 在网上找了很多方法,最终的方法基本都是改动画规则,比如 @keyfr ...
- vs遇到的字符串问题
原以为自己的字符串已经理解不错了, 今天又被vs搞了. 情景就不说了, 直接说结果: 有两种情况 1 当文件是存储为gbk或者utf-8的时候, 中文字符存储永远是gbk的值. ‘按’字的gbk编码 ...
- Jenkins Pipeline 部署 SpringBoot 应用
一. 安装依赖包 yum install -y wget yum install -y gcc-c++ yum install -y zlib-devel perl-ExtUtils-MakeMake ...