C++哈希-使用/模拟/封装(1)

简介: C++哈希-使用/模拟/封装(1)

零、前言


本章主要讲解unordered系列关联式容器及其底层结构和模拟实现,还有哈希的相关应用等


一、unordered系列关联式容器


概念:


在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 ,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想

最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同


unordered_map/unordered_set与map/set基本上只有底层实现上的区别,前者是哈希,后者是红黑树


unordered_map/unordered_set与unordered_multimap/unordered_multiset的区别是是否允许键值冗余


unordered系列关联式容器因为底层不是红黑树了,所以遍历的结果不是排序好的序列


1、unordered_map介绍及使用


概念:


unordered_map是存储<key, value>键值对的关联式容器,其允许通过key值快速的索引到与其对应是value


在unordered_map中,键值通常用于唯一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同


在内部,unordered_map没有对<key, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中

unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低


unordered_map实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value


它的迭代器是单向正向迭代器



接口介绍:

unordered_map的构造


函数声明 功能介绍
unordered_map 构造不同格式的unordered_map对象


  1. unordered_map的容量


函数声明 功能介绍
bool empty() const 检测unordered_map是否为空
size_t size() const 获取unordered_map的有效元素个数


  1. unordered_map的迭代器


函数声明 功能介绍
begin 返回unordered_map第一个元素的迭代器
end 返回unordered_map最后一个元素下一个位置的迭代器
cbegin 返回unordered_map第一个元素的const迭代器
cend 返回unordered_map最后一个元素下一个位置的const迭代器


  1. unordered_map的元素访问


函数声明 功能介绍
operator[] 返回与key对应的value,没有一个默认值


注意:该函数中实际调用哈希桶的插入操作,用参数key与V()构造一个默认值往底层哈希桶中插入,如果key不在哈希桶中,插入成功,返回V(),插入失败,说明key已经在哈希桶中,将key对应的value返回


  1. unordered_map的查询


函数声明 功能介绍
iterator find(const K& key) 返回key在哈希桶中的位置
size_t count(const K& key) 返回哈希桶中关键码为key的键值对的个数



注意:unordered_map中key是不能重复的,因此count函数的返回值最大为 1,对于unordered_multimap才是允许键值冗余的


  1. unordered_map的修改操作


函数声明 功能介绍
insert 向容器中插入键值对
erase 删除容器中的键值对
void clear() 清空容器中有效元素个数
void swap(unordered_map&) 交换两个容器中的元素


  1. unordered_map的桶操作


函数声明 功能介绍
size_t bucket_count()const 返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const 返回n号桶中有效元素的总个数
size_t bucket(const K& key) 返回元素key所在的桶号



  • 使用示例:


void test_unordered_map()
{
  unordered_map<string, string> dict;
  dict.insert(make_pair("string", ""));
  dict.insert(make_pair("sort", ""));
  auto it = dict.begin();
  while (it != dict.end())
  {
    cout << it->first << ":" << it->second << endl;
    ++it;
  }
  cout << endl;
}


image.png


2、unordered_set的介绍及使用



概念:


unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素


在unordered_set中,元素的值同时也是唯一地标识它的key


在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中


unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低


它的迭代器是单向前向迭代器


接口介绍:


unordered_set当中常用的成员函数


成员函数 功能


insert 插入指定元素

erase 删除指定元素

find 查找指定元素


size 获取容器中元素的个数

empty 判断容器是否为空

clear 清空容器


swap 交换两个容器中的数据

count 获取容器中指定元素值的元素个数

迭代器相关函数


成员函数 功能


begin 获取容器中第一个元素的正向迭代器

end 获取容器中最后一个元素下一个位置的正向迭代器

使用示例:


void test_unordered_set()
{
  unordered_set<int> s;
  s.insert(3);
  s.insert(4);
  s.insert(5);
  s.insert(3);
  s.insert(1);
  s.insert(2);
  s.insert(6);
  unordered_set<int>::iterator it = s.begin();
  while (it != s.end())
  {
    cout << *it << " ";
    ++it;
  }
  cout << endl;
}


image.png


3、性能比较

  • 测试代码:


void test_op()
{
  set<int> s;
  unordered_set<int> us;
  const int n = 1000000;
  vector<int> v;
  srand(time(0));
  for (size_t i = 0; i < n; ++i)
  {
    v.push_back(rand());
  }
  size_t begin1 = clock();
  for (auto e : v)
  {
    s.insert(e);
  }
  size_t end1 = clock();
  size_t begin2 = clock();
  for (auto e : v)
  {
    us.insert(e);
  }
  size_t end2 = clock();
  cout << "set insert:" << end1 - begin1 << endl;
  cout << "unordered_set insert:" << end2 - begin2 << endl;
  cout << "=====================" << endl;
  size_t begin3 = clock();
  for (auto e : v)
  {
    s.find(e);
  }
  size_t end3 = clock();
  size_t begin4 = clock();
  for (auto e : v)
  {
    us.find(e);
  }
  size_t end4 = clock();
  cout << "set find:" << end3 - begin3 << endl;
  cout << "unordered_set find:" << end4 - begin4 << endl;
  cout << "=====================" << endl;
  size_t begin5 = clock();
  for (auto e : v)
  {
    s.erase(e);
  }
  size_t end5 = clock();
  size_t begin6 = clock();
  for (auto e : v)
  {
    us.erase(e);
  }
  size_t end6 = clock();
  cout << "set erase:" << end5 - begin5 << endl;
  cout << "unordered_set erase:" << end6 - begin6 << endl;
}


总结:使用底层为哈希的容器总体的效率是非常高的,对于关联式set/map的复杂度能达到O(logN),而unordered系列关联式容器可以达到接近O(1)的水平



相关文章
|
4天前
|
存储 编译器 C++
C++:map&set 对红黑树的封装
C++:map&set 对红黑树的封装
11 1
|
4天前
|
存储 算法 数据安全/隐私保护
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
7 0
|
4天前
|
存储 Serverless C++
【C++入门到精通】哈希 (STL) _ unordered_map _ unordered_set [ C++入门 ]
【C++入门到精通】哈希 (STL) _ unordered_map _ unordered_set [ C++入门 ]
10 1
|
4天前
|
存储 算法 C++
【C++高阶(六)】哈希的应用--位图&布隆过滤器
【C++高阶(六)】哈希的应用--位图&布隆过滤器
|
4天前
|
存储 Serverless C++
【C++高阶(五)】哈希思想--哈希表&哈希桶
【C++高阶(五)】哈希思想--哈希表&哈希桶
|
4天前
|
C++
C++ 访问说明符详解:封装数据,控制访问,提升安全性
C++ 中的访问说明符(public, private, protected)用于控制类成员的可访问性,实现封装,增强数据安全性。public 成员在任何地方都可访问,private 只能在类内部访问,protected 则允许在类及其派生类中访问。封装提供数据安全性、代码维护性和可重用性,通过 setter/getter 方法控制对私有数据的访问。关注公众号 `Let us Coding` 获取更多内容。
27 1
|
4天前
|
编译器 C语言 C++
【C++成长记】C++入门 | 类和对象(上) |面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装
【C++成长记】C++入门 | 类和对象(上) |面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装
|
4天前
|
存储 编译器 程序员
【C++】类和对象①(什么是面向对象 | 类的定义 | 类的访问限定符及封装 | 类的作用域和实例化 | 类对象的存储方式 | this指针)
【C++】类和对象①(什么是面向对象 | 类的定义 | 类的访问限定符及封装 | 类的作用域和实例化 | 类对象的存储方式 | this指针)
|
4天前
|
C++
【C++】C++封装成DLL并调用(初学者快速入门)
【C++】C++封装成DLL并调用(初学者快速入门)
|
4天前
|
JSON Linux API
一个C++版本的Sqlite3封装--SmartDb
一个C++版本的Sqlite3封装--SmartDb
21 0