关联式容器-基于hash

5.7 hashtable

1、碰撞问题的解决

线性探测:负载系数在0-1之间(元素个数除以表格大小)。

二次探测:如果位置已被占用,则依序尝试H+1^2, H+2^2, ... , H+i^2,而不是像线性探测那样尝试H+1, H+2 ... , H+i。

开链法:负载系数大于1。


2、哈希表的bucket和node

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

3、哈希表的迭代器

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_const_iterator<Value, Key, HashFcn, 
                                     ExtractKey, EqualKey, Alloc>
          const_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;   //保持对容器的连结关系(因为可能需要从bucket跳到bucket)

  __hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
  __hashtable_iterator() {}
  reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
  iterator& operator++();
  iterator operator++(int);
  bool operator==(const iterator& it) const { return cur == it.cur; }
  bool operator!=(const iterator& it) const { return cur != it.cur; }
};

template <class V, class K, class HF, class ExK, class EqK, class A>
__hashtable_const_iterator<V, K, HF, ExK, EqK, A>&
__hashtable_const_iterator<V, K, HF, ExK, EqK, A>::operator++()
{
  const node* old = cur;
  cur = cur->next;
  if (!cur) {
    size_type bucket = ht->bkt_num(old->val);  //定位出下一个bucket
    while (!cur && ++bucket < ht->buckets.size())
      cur = ht->buckets[bucket];
  }
  return *this;
}

4、hashtable的数据结构

template <class Value, class Key, class HashFcn,
          class ExtractKey, class EqualKey,
          class Alloc>
class hashtable {
public:
  typedef Key key_type;
  typedef Value value_type;
  typedef HashFcn hasher;    //hash function的函数型别
  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 hash; }
  key_equal key_eq() const { return equals; }

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:
  typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, 
                               Alloc>
  iterator;

  typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
                                     Alloc>
  const_iterator;

  friend struct
  __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
  friend struct
  __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
...
};

5、hashtable的构造与内存管理

hashtable(size_type n,
          const HashFcn&    hf,
          const EqualKey&   eql,
          const ExtractKey& ext)
  : hash(hf), equals(eql), get_key(ext), num_elements(0)
{
  initialize_buckets(n);
}
void initialize_buckets(size_type n)
{
  const size_type n_buckets = next_size(n);  //计算出n个元素应当分配多大的哈希表
  buckets.reserve(n_buckets);                //buckets是用vector实现的,先预留容量
  buckets.insert(buckets.end(), n_buckets, (node*) 0);
  num_elements = 0;
}


插入分为insert_unique()和insert_equal()。
注意:SGI hashtable的hash仿函数(hash<...>)只能处理short、int、long、char *、const  char*、char,不能处理string、float、double。欲处理这些型别,用户必须自行定义它们的hash function


5.8 hash_set

由于RB-tree有自动排序功能而hashtable没有,所以set的元素有自动排序功能而hash_set没有。除此之外,hash_set的使用方式与set完全相同。

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class Value, class HashFcn = hash<Value>,
          class EqualKey = equal_to<Value>,
          class Alloc = alloc>
#else
template <class Value, class HashFcn, class EqualKey, class Alloc = alloc>
#endif
class hash_set
{
private:
  typedef hashtable<Value, Value, HashFcn, identity<Value>, 
                    EqualKey, Alloc> ht;
  ht rep;

public:
  typedef typename ht::key_type key_type;
  typedef typename ht::value_type value_type;
  typedef typename ht::hasher hasher;
  typedef typename ht::key_equal key_equal;

  typedef typename ht::size_type size_type;
  typedef typename ht::difference_type difference_type;
  typedef typename ht::const_pointer pointer;                 //注意不可以直接修改元素的值
  typedef typename ht::const_pointer const_pointer;      
  typedef typename ht::const_reference reference;
  typedef typename ht::const_reference const_reference;

  typedef typename ht::const_iterator iterator;
  typedef typename ht::const_iterator const_iterator;

  hasher hash_funct() const { return rep.hash_funct(); }
  key_equal key_eq() const { return rep.key_eq(); }

public:
  hash_set() : rep(100, hasher(), key_equal()) {}    //缺省使用大小为100的表格,将被hashtable调整为最接近且较大的质数。
  explicit hash_set(size_type n) : rep(n, hasher(), key_equal()) {}
  hash_set(size_type n, const hasher& hf) : rep(n, hf, key_equal()) {}
  hash_set(size_type n, const hasher& hf, const key_equal& eql)
    : rep(n, hf, eql) {}

  void swap(hash_set& hs) { rep.swap(hs.rep); }
  friend bool operator== __STL_NULL_TMPL_ARGS (const hash_set&,
                                               const hash_set&);

  iterator begin() const { return rep.begin(); }
  iterator end() const { return rep.end(); }

public:
  pair<iterator, bool> insert(const value_type& obj)
    {
      pair<typename ht::iterator, bool> p = rep.insert_unique(obj);
      return pair<iterator, bool>(p.first, p.second);
    }
  ...
};

5.9 hash_map

同样无排序。

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class Key, class T, class HashFcn = hash<Key>,
          class EqualKey = equal_to<Key>,
          class Alloc = alloc>
#else
template <class Key, class T, class HashFcn, class EqualKey, 
          class Alloc = alloc>
#endif
class hash_map
{
private:
  typedef hashtable<pair<const Key, T>, Key, HashFcn,
                    select1st<pair<const Key, T> >, EqualKey, Alloc> ht;
  ht rep;

public:
  typedef typename ht::key_type key_type;
  typedef T data_type;
  typedef T mapped_type;
  typedef typename ht::value_type value_type;
  typedef typename ht::hasher hasher;
  typedef typename ht::key_equal key_equal;

  typedef typename ht::size_type size_type;
  typedef typename ht::difference_type difference_type;
  typedef typename ht::pointer pointer;
  typedef typename ht::const_pointer const_pointer;
  typedef typename ht::reference reference;
  typedef typename ht::const_reference const_reference;

  typedef typename ht::iterator iterator;
  typedef typename ht::const_iterator const_iterator;

  hasher hash_funct() const { return rep.hash_funct(); }
  key_equal key_eq() const { return rep.key_eq(); }

public:
  hash_map() : rep(100, hasher(), key_equal()) {}
  explicit hash_map(size_type n) : rep(n, hasher(), key_equal()) {}
  hash_map(size_type n, const hasher& hf) : rep(n, hf, key_equal()) {}
  hash_map(size_type n, const hasher& hf, const key_equal& eql)
    : rep(n, hf, eql) {}

  T& operator[](const key_type& key) {
    return rep.find_or_insert(value_type(key, T())).second;
  }  
  ...
};

5.10 hash_multiset

采用insert_equal()。


5.11 hash_multimap

采用insert_equal()。


<think>我们正在回答用户的问题:C++中什么是序列容器、关联容器、无序容器 根据之前的对话历史,用户已经了解容器支持列表初始化,现在想深入理解容器的分类概念。 我们将结合引用内容,分别解释这三类容器,并给出示例和关键特性。 引用说明: [^1] 提到关联容器的关键特性:基于键的索引、迭代器、唯一性、重复键、排序等。 [^2] 提到迭代器的分类和不同容器的迭代器能力差异。 [^3] 提到set的模板定义,体现了关联容器的模板参数。 我们将按照以下结构组织回答: 1. 序列容器(Sequence Containers) 2. 关联容器(Associative Containers) 3. 无序容器(Unordered Associative Containers) 注意:在回答中,我们将使用LaTeX格式行内数学表达式(如$O(\log n)$)和独立公式(如复杂度公式),并确保引用标记正确。</think>### C++ 容器分类详解 #### 1. **序列容器 (Sequence Containers)** 存储元素按线性顺序排列,元素位置由插入顺序决定,支持随机访问或顺序访问。 **核心特性**: - 元素按插入顺序存储 - 支持在任意位置插入/删除 - 迭代器类型:随机访问(vector, deque)或双向(list)[^2] - 时间复杂度: - 随机访问:$O(1)$ (vector, deque) - 插入/删除:$O(n)$ (平均情况) **常用容器**: ```cpp #include <vector> #include <deque> #include <list> #include <forward_list> #include <array> // 示例 std::vector<int> vec{1,2,3}; // 动态数组 std::list<std::string> names{"Alice", "Bob"}; // 双向链表 std::array<float, 4> arr{1.1, 2.2}; // 固定数组 ``` #### 2. **关联容器 (Associative Containers)** 基于键值对存储,元素自动排序,支持高效查找 ($O(\log n)$)。 **核心特性**[^1][^3]: - 基于键的索引(键不可修改) - 元素自动按键排序(默认 `std::less<Key>`) - 键唯一性:`set/map` 键唯一,`multiset/multimap` 允许多重键 - 迭代器类型:双向迭代器 - 底层实现:红黑树 **常用容器**: ```cpp #include <set> #include <map> #include <multiset> #include <multimap> // 示例 std::set<int> unique_ids{101,102}; // 唯一键集合 std::map<int, std::string> employees{ {101, "Alice"}, {102, "Bob"} // 键值对映射 }; ``` #### 3. **无序容器 (Unordered Associative Containers)** 基于哈希表实现,元素无序存储,提供平均 $O(1)$ 的查找性能。 **核心特性**: - 基于哈希值的元素存储 - 元素无序(依赖哈希函数) - 键唯一性:`unordered_set/map` 键唯一,`unordered_multiset/map` 允许多重键 - 迭代器类型:前向迭代器 - 性能可能退化到 $O(n)$(哈希冲突时) **常用容器**: ```cpp #include <unordered_set> #include <unordered_map> // 示例 std::unordered_set<std::string> languages{"C++", "Python"}; std::unordered_map<int, float> product_prices{ {1001, 9.99}, {1002, 19.50} }; ``` ### 三类容器对比表 | 特性 | 序列容器 | 关联容器 | 无序容器 | |---------------------|-----------------------|-----------------------|-----------------------| | **元素顺序** | 插入顺序 | 键排序 | 哈希值决定 | | **查找复杂度** | $O(n)$ | $O(\log n)$ | 平均 $O(1)$ | | **迭代器能力** | 随机访问/双向[^2] | 双向 | 前向 | | **键唯一性** | 不适用 | 可配置(唯一/多重) | 可配置(唯一/多重) | | **底层实现** | 数组/链表 | 红黑树 | 哈希桶 | | **典型应用** | 需要顺序访问的场景 | 需要有序数据的场景 | 需要快速查找的场景 | ### 关键差异解析 1. **迭代器能力**[^2] - 序列容器(如 `vector`)支持随机访问迭代器(`it+3`) - 关联/无序容器仅支持双向/前向迭代器(`++it`/`--it`) ```cpp std::vector<int> v{1,2,3}; auto it_vec = v.begin() + 2; // ✅ 随机访问 std::set<int> s{1,2,3}; auto it_set = s.begin()++; // auto it_err = s.begin() + 2; // ❌ 编译错误 ``` 2. **排序机制** - 关联容器自动排序(可自定义比较函数)[^3]: ```cpp std::set<int, std::greater<int>> desc_set{3,1,2}; // {3,2,1} ``` - 无序容器依赖哈希函数: ```cpp struct CaseInsensitiveHash { size_t operator()(const std::string& s) const { return std::hash<std::string>{}(tolower(s)); } }; std::unordered_set<std::string, CaseInsensitiveHash> unique_words; ``` 3. **性能权衡** - 序列容器:插入快(尾部)、查找慢 - 关联容器:插入/查找平衡($O(\log n)$) - 无序容器:查找最快($O(1)$),但内存开销大 > 引用文献: > [^1]: 关联容器基于键索引,保证元素有序性 > [^2]: 迭代器能力决定算法兼容性(如排序算法需随机访问迭代器) > [^3]: 关联容器默认使用 `std::less<Key>` 排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值