【STL】RB-tree(红黑树)和hashtable(哈希表)的实现原理

今日记

近来,事情颇多,微烦。心想:STL离我远去,荣光渐失。但夜不能寐,终究是抵不过。罢了,罢了,今日无事,就随她吧!这个磨人的***!

当再次翻开《STL源码剖析》,已无往日思绪。唉~

概述

STL容器,放置物品的东西。所谓STL容器,即是一些常用的数据结构实现出来。

容器分为关联式容器和序列容器,而关联容器分为set(集合)和map(映射表)两大类,以及衍生出来的multiset(多键集合)和multimap(多键映射表)。这些容器的底层机制有两种实现方式,红黑树和哈希表。同时,他们也是一种独立容器,但并不对外开放。

所谓关联式容器,每个元素都有一个键值(key)和实值(value),插入后,会以某种规定放置在适当位置。同时,关联式容器没有头尾,所以没有push_back、pop_back等操作。

本章所采用版本为 SGI STL 2.91版本。

RB-tree(红黑树)

树的概况

树,在计算机科学中是一种很基础的数据结构,用途相当广泛。树同属于指针三剑客之一,由结点和边构成。同时,要了解路径长度、深度、高度等概念。

二查搜索树

二叉树,即根节点只有左右两个子节点,并且具有放置规则:任何结点的键值一定大于其左子树的每一个结点的键值,并且小于其右子树中每一个结点的键值。这样就可以提供一个对数时间来进行元素插入和访问。

要删除A的一个结点,若A只有一个子结点可以直接将A的子结点连接到A的父结点。若A有两个子结点,就将右子树最小节点取代A。

要插入新值的话,遇到比当前键值大则向右,否则向左,直到尾端。

平衡二叉搜索树

因为输入值不随机或者某些操作导致二叉树失去平衡,就会造成效率低落的情况。如图所示:
在这里插入图片描述

AVL tree

AVL tree是一个“加上额外平衡条件”的二叉搜索树。其平衡条件的建立是为了确保整棵树的深度为O(logN)。要求任何结点的左右子树高度相差最多为1。

当出现插入点改变平衡状态时,只需要调整最深的那个节点,便可使整棵树获得平衡。例如:
在这里插入图片描述
由于插入点会破坏平衡,假设最深结点为X,由于X最多有两个结点,所以平衡被破坏后,左右子树高度相差2,会出现四种情况:

  1. 插入点位于X的左子节点的左子树-左左
  2. 插入点位于X的左子节点的右子树-左右
  3. 插入点位于X的右子节点的左子树-右左
  4. 插入点位于X的右子节点的右子树-右右

1,4 称为外侧插入,可以使用单旋转操作。2,3称为内侧插入,可以采用双旋转操作。

在这里插入图片描述

单旋转

在这里插入图片描述

双旋转

双旋转即是通过两次单旋转完成操作。
在这里插入图片描述
在这里插入图片描述

红黑树(RB-tree)

红黑树,不仅是二叉搜索树,并且要满足以下规则:

  1. 每个节点不是红色就是黑色
  2. 根节点为黑色
  3. 如果节点为红,其子节点必须是黑
  4. 任一节点至NULL的任何路径,所含黑节点数目必须相同。

根据上述规则,新增节点必须为红;新增节点父结点必须是黑。当不能满足时,就需要进行调整颜色并旋转。
在这里插入图片描述
由于,插入删除操作过于复杂,本人能力有限,这部分先不谈了。

RB-tree的节点设计

typedef bool _Rb_tree_Color_type;
const _Rb_tree_Color_type _S_rb_tree_red = false;
const _Rb_tree_Color_type _S_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 _M_color; 
  _Base_ptr _M_parent;
  _Base_ptr _M_left;
  _Base_ptr _M_right;

  static _Base_ptr _S_minimum(_Base_ptr __x)
  {
    while (__x->_M_left != 0) __x = __x->_M_left;
    return __x;
  }

  static _Base_ptr _S_maximum(_Base_ptr __x)
  {
    while (__x->_M_right != 0) __x = __x->_M_right;
    return __x;
  }
};

template <class _Value>
struct _Rb_tree_node : public _Rb_tree_node_base
{
  typedef _Rb_tree_node<_Value>* _Link_type;
  _Value _M_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 = y->parent;
         	}
       	    if (node->right != y)      // 若此时该节点不为它的父节点的右子节点
              	 node = y;                // 此时的父节点即为要找的后继节点
                                 // 否则此时的node即为要找的后继节点,此为特殊情况,如下
                                 // 我们要寻找根节点的下一个节点,而根节点没有右子节点
                                 // 此种情况需要配合rbtree的header节点的特殊设计,后面会讲到
      	 }                        
 	 }

  	// 寻找该节点你的前置节点
	  void decrement()
  	  {
 		   if (node->color == __rb_tree_red && // 如果此节点是红节点
       			 node->parent->parent == node)       // 且父节点的父节点等于自己
     			 node = node->right;                               // 则其右子节点即为其前置节点
    // 以上情况发生在node为header时,即node为end()时
    // 注意:header的右子节点为mostright,指向整棵树的max节点,后面会有解释
   		   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
{
	 //...型别声明
     // 迭代器的构造函数
 	 __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 &(operator*()); }
	  // 前置++和后置++
	  self& operator++() { increment(); return *this; }
	  self operator++(int) {
  	  	self tmp = *this;
  	 	increment();        // 直接调用increment函数
   		return tmp;
 	  }
 	 // 前置--和后置--
	  self& operator--() { decrement(); return *this; }
 	  self operator--(int) {
   		  self tmp = *this;
  		  decrement();        // 直接调用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;
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;  // 节点间的键值大小比较准则

  // 以下三个函数用来取得header的成员
  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& left(link_type x) { return (link_type&)(x->left); }
  static link_type& right(link_type x) { return (link_type&)(x->right); }
  static link_type& parent(link_type x) { return (link_type&)(x->parent); }
  static reference value(link_type x) { return x->value_field; }
  static const Key& key(link_type x) { return KeyOfValue()(value(x)); }
  static color_type& color(link_type x) { return (color_type&)(x->color); }

  // 以下六个函数用来取得节点的成员,由于双层设计,导致这里需要两个定义
  static link_type& left(base_ptr x) { return (link_type&)(x->left); }
  static link_type& right(base_ptr x) { return (link_type&)(x->right); }
  static link_type& parent(base_ptr x) { return (link_type&)(x->parent); }
  static reference value(base_ptr x) { return ((link_type)x)->value_field; }
  static const Key& key(base_ptr x) { return KeyOfValue()(value(link_type(x)));} 
  static color_type& color(base_ptr x) { return (color_type&)(link_type(x)->color); }

  // 求取极大值和极小值,这里直接调用节点结构的函数极可
  static link_type minimum(link_type x) { 
    return (link_type)  __rb_tree_node_base::minimum(x);
  }
  static link_type maximum(link_type x) {
    return (link_type) __rb_tree_node_base::maximum(x);
  }

public:
    // RBTree的迭代器定义
  typedef __rb_tree_iterator<value_type, reference, pointer> iterator;
  typedef __rb_tree_iterator<value_type, const_reference, const_pointer> 
          const_iterator;
private:
	//...
	void init() {   //构造一个空tree
		header = get_node();   //产生一个节点空间,令header指向它
		color(header) = __rb_tree_red;  //令header为红色,用来将
		                                //root与header区分开
		root() = 0;          
		leftmost() = header;       //header的左子节点为自己
		rightmost() = header;      //header的右子节点为自己
	}
public:
  Compare key_comp() const { return key_compare; }  // 由于红黑树自带排序功能,所以必须传入一个比较器函数
  iterator begin() { return leftmost(); }        // RBTree的起始节点为左边最小值节点
  const_iterator begin() const { return leftmost(); }
  iterator end() { return header; }                         // RBTree的终止节点为右边最大值节点
  const_iterator end() const { return header; }
  bool empty() const { return node_count == 0; }    // 判断红黑树是否为空    
  size_type size() const { return node_count; }     // 获取红黑树的节点个数
  size_type max_size() const { return size_type(-1); }  // 获取红黑树的最大节点个数,
                                                  // 没有容量的概念,故为sizetype最大值
};

作为map及set的底层数据结构,RB-tree的重要性不言而喻,而且面试时几乎是必考题,所以掌握RB-tree会受益匪浅

hashtable(哈希表)

二叉搜索树具有对数平均时间的查找表现,但他建立在一个假设:输入数据具有足够随机性。但哈希表也具有这种“常熟平均时间”的表现,不需依赖元素的随机性。

hashtable概述

hash table可以提供对任何有名项的存取和删除操作。例如:一个数组A,存放10个数字,便可对其常数时间操作,A[i]++等。而有名项的范围就很广,16-bits 的整数就有0~65535,还要包括字符串、char等类型。这就遇到两个现实问题:第一,要想把所有有名项表示一边,需要很大的空间,不切实际。第二,字符串等类型无法作为数组的索引。

对于第一个问题,解决办法之一就是使用某种映射函数,将大数变成小数。该函数被称为hash function。第二个问题,可以将一些字符串或者其他类型通过某种形式转化成整型作为索引。

但与之而来另一个问题就是,当两个数字映射到同一位置怎么办?这就是“碰撞”问题即“哈希冲突”。解决这种碰撞有很多方法,包括:

  • 开放定址法:所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
  • 再哈希法: 再哈希法又叫双哈希法,有多个不同的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数,计算地址,直到无冲突。虽然不易发生聚集,但是增加了计算时间。
  • 链地址法: 每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来。
  • 建立公共溢出区: 将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

比较常用的是链地址法。

hashtable的桶子与节点

下图即是链地址法的一种结构。包括桶子和存储数据的链表。
在这里插入图片描述
下面是节点定义:

template<calss Value>
struct __hashtable_node
{
	__hashtable_node* next;
	Value val;
}

如图所示:
在这里插入图片描述
这里存储数据的链表并非STL里的list或slist,而是自行维护的node。桶子则是由vector完成,拥有动态扩充的能力。

hashtable的迭代器

hashtable的迭代器维持整个“bucket vector”之间的关系,前进操作是尝试当前结点的下一个位置,如果到达list的尾端,就跳到下一个bucket身上。同时,不提供倒退的操作。


template <class _Val, class _Key, class _HashFcn, class _ExtractKey, class _EqualKey, class _Alloc>
struct _Hashtable_iterator {
  typedef hashtable<_Val,_Key,_HashFcn,_ExtractKey,_EqualKey,_Alloc> _Hashtable;
  typedef _Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc> iterator;
  typedef _Hashtable_const_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc> const_iterator;
  typedef _Hashtable_node<_Val> _Node;

  typedef forward_iterator_tag iterator_category;
  typedef _Val value_type;
  typedef ptrdiff_t difference_type;
  typedef size_t size_type;
  typedef _Val& reference;
  typedef _Val* pointer;

  _Node* _M_cur;    	//迭代器目前所指的节点
  _Hashtable* _M_ht;	//保持对容器的连接关系

  _Hashtable_iterator(_Node* __n, _Hashtable* __tab) : _M_cur(__n), _M_ht(__tab) {}
  _Hashtable_iterator() {}
  reference operator*() const { return _M_cur->_M_val; }
  iterator& operator++();
  iterator operator++(int);
  bool operator==(const iterator& __it) const{ return _M_cur == __it._M_cur; }
  bool operator!=(const iterator& __it) const{ return _M_cur != __it._M_cur; }
};

operator++的非const版本

template <class _Val, class _Key, class _HF, class _ExK, class _EqK, class _All>
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>&_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>::operator++()
{
  const _Node* __old = _M_cur;
  _M_cur = _M_cur->_M_next;
  if (!_M_cur) {
    size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val);
    while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size())
      _M_cur = _M_ht->_M_buckets[__bucket];
  }
  return *this;
}

template <class _Val, class _Key, class _HF, class _ExK, class _EqK,  class _All>
inline _Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>::operator++(int)
{
  iterator __tmp = *this;
  ++*this;
  return __tmp;
}

hashtable的数据结构

下面是hashtable的部分定义摘要:

template <class _Val, class _Key, class _HashFcn, class _ExtractKey, class _EqualKey, class _Alloc>
class hashtable {
public:
  typedef _Key key_type;
  typedef _Val value_type;
  typedef _HashFcn hasher;
  typedef _EqualKey key_equal;

  typedef size_t            size_type;
  typedef ptrdiff_t         difference_type;
  typedef value_type*       pointer;
  typedef const value_type* const_pointer;
  typedef value_type&       reference;
  typedef const value_type& const_reference;

  hasher hash_funct() const { return _M_hash; }
  key_equal key_eq() const { return _M_equals; }

private:
  typedef _Hashtable_node<_Val> _Node;

private:
  hasher                _M_hash;
  key_equal             _M_equals;
  _ExtractKey           _M_get_key;
  vector<_Node*,_Alloc> _M_buckets;    			//hashtable的桶是由vector实现的
  size_type             _M_num_elements;
  ...

public:

  size_type bucket_count() const { return _M_buckets.size(); }
  .....
};

其中,模板的几个参数分别代表:

  • _Val:节点的值的类型
  • _Key:节点键值类型
  • _HashFcn:哈希函数的型别
  • _ExtractKey:从节点中取出键值的方法
  • _EqualKey:判断键值是否相同的方法
  • _Alloc:空间配置器

如果桶满了怎么办,SGI提供了一个质数来设计表格大小,以备随时访问,进行扩充。当然,这只是SGI这么做的,并不确保所有STL都是这么做的。

enum { __stl_num_primes = 28 };

static const unsigned long __stl_prime_list[__stl_num_primes] =
{
  53ul,         97ul,         193ul,       389ul,       769ul,
  1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
  49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
  1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
  50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul, 
  1610612741ul, 3221225473ul, 4294967291ul
};

inline unsigned long __stl_next_prime(unsigned long __n)
{
  const unsigned long* __first = __stl_prime_list;
  const unsigned long* __last = __stl_prime_list + (int)__stl_num_primes;
  const unsigned long* pos = lower_bound(__first, __last, __n);
  return pos == __last ? *(__last - 1) : *pos;
}

hashtable的构造与内存管理

  • 节点配置与释放
node* new_node(const value_type& obj)
{
	node* n = node_allocator::allocate();
	n->next = 0;
	__STL_TRY {
		construct(&n->val, obj);
		return n;
	}
	__STL_UNWIND(node_allocator::deallocate(n));
}
void delete_node(node* n)
{
	destroy(&n->val);
	node_alllocaor::deallocate(n);
}
  • hashtable构造:
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)
{
	buckets.reserve(n_bckets);
	buckets.insert(buckets.end(), n_buckets, (node*) 0);
	num_elements = 0;
}

  • 插入元素:分为两种,insert_unique与insert_equal
// 插入操作,不允许重复
  pair<iterator, bool> insert_unique(const value_type& obj)
  {
   	  resize(num_elements + 1);
  	  return insert_unique_noresize(obj);
  }

template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
pair<typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::iterator, bool>
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::insert_unique_noresize(const value_type& obj)
{
 	  const  size_type  n = bkt_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;        //将新节点插入链表头部
  	 buckets[n] = tmp;
 	 ++num_elements;      //节点个数累计加1
  	 return pair<iterator, bool>(iterator(tmp, this), true); ///返回一个迭代器,指向新增节点
}


 //插入操作,允许重复
  iterator insert_equal(const value_type& obj)
  {
   	    resize(num_elements + 1);
    	return insert_equal_noresize(obj);
  }
template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
pair<typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::iterator, bool>
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::insert_equal_noresize(const value_type& obj)
{
 	  const  size_type  n = bkt_num(obj);
	  node*  first = buckets[n];
 
  // 如果已经存在则直接插入并返回
  for (node* cur = first; cur;  cur = cur->_next)
    	if (equals(get_key(cur->val), get_key(obj))) {
    		 node* tmp = new_node(obj);  //产生新节点
  			 tmp->next = first;        //将新节点插入链表头部
  			 buckets[n] = tmp;
 			 ++num_elements;      //节点个数累计加1
  			 return pair<iterator, bool>(iterator(tmp, this), true); ///返回一个迭代器,指向新增节点
 		}
    		  
 
  // 表示没有发现新节点
	 node* tmp = new_node(obj);  //产生新节点
  	 tmp->next = first;        //将新节点插入链表头部
  	 buckets[n] = tmp;
 	 ++num_elements;      //节点个数累计加1
  	 return pair<iterator, bool>(iterator(tmp, this), true); ///返回一个迭代器,指向新增节点
}


//判断是否需要重建表格
template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
  ::resize(size_type num_elements_hint)
{
 	 const size_type old_n = buckets.size();
 	 // 超过原来表格的大小时才进行调整
  	 if (__num_elements_hint > old_n) {
    		// 新的表格大小
   		 const size_type  n = next_size(num_elements_hint);
 		   // 在边界情况下可能无法调整(没有更大的素数了)
   		 if (n > old_n) {
      		vector<node*, All> tmp(n, (node*)(0),
     	    __STL_TRY {
       	 // 填充新的表格
     		   for (size_type bucket = 0;bucket < old_n; ++bucket) {
     	     		  node* first = buckets[bucket];  //指向节点所对应的起始节点
       	       		  while (first) {
       	       		  			//以下找出节点落在哪一个新bucket内
             				   size_type __new_bucket = bkt_num(first->val, n);
             				   	//令旧节点指向对应串行的下一个节点
         					   buckets[bucket] = first->next;
         					   //将当前节点插入到新bucket内
          				       first->next = tmp[new_bucket];
               				   tmp[new_bucket] = first;
               				   //准备处理下一个节点
          	   				   first = buckets[bucket];
       				   }
     		  }
        // 通过swap交换
  	      buckets.swap(tmp); 
     	   }
   	   }
    }
}
  • 判知元素的落脚处:
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 value_type& key)  const {
	return bkt_num_key(key, buckets.size());
}
size_type bkt_num_key(const value_type& key,szie_t n)  const {
	return hash(key) % n;
}

元素操作

操作功能
copy_from复制整个hashtable
clearhashtable整体删除
find搜寻键值为key的元素
count计算键值为key的元素个数

hash functions

hash function是计算元素位置的函数,SGI将这项任务交给bkt_num函数,然后再调用下列提供的hash function,取得一个可以对hashtable进行模运算的值

template <class Key> struct hash{}
//以下这个转换函数还没看太懂?????
inline size_t __stl_hash_string(const char* s)
{
	unsigned long h  = 0;
	for (; *s; ++s)
		h = 5*h + *s;
	return size_t(h);
}

template <>
__STL_TEMPLATE_NULL struct hash<char*>
{
	size_t operator()(const char *s)  const { return __stl_hash_string(s); }
};
//...

总结

红黑树与哈希表作为关联式容器的底层容器,重要性不言而喻,本人对于其理解非常浅薄,本文只是粗略的介绍,并未有实质的东西,望谅解。

参考资料:
《STL源码剖析》 - 侯捷

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值