STL之哈希表

C++STL-无序容器-哈希表(散列存储) 1. 为什么要设计哈希表——查找元素效率高 O(1) 2. 通过什么机制实现的——关键字通过哈希函数O(1)可以直接确定哈希地址,常见的有直接定址法、除留余数法 3. 会产生什么问题——不同的关键字可能会得到相同的哈希地址,称为哈希碰撞 4. 如何解决哈希碰撞——闭散列找空位置(每次向后找1或i^2),或者开散列建哈希桶** 阅读详情


一、哈希表

1.1、哈希表相关概念

对于线性表或树等数据结构,记录在数据结构中的相对位置是随机的,和记录的关键字之间不存在确定的关系,以此,在结构中查找记录时需要进行一系列和关键字的比较。理想的情况是希望不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置和它的关键字之间建立一个确定的对应关系 f,使每个关键字和结构中一个唯一的存储位置相对应。因而在查找时,只要根据这个对应关系 f 找到给定值 K 的像 f(K)。若结构中存在关键字和 K 相等的记录,则必定在 f(K)的存储位置上,由此,不需要进行比较便可直接取得所查记录。我们称这个对应关系 f 为哈希(hash)函数,按这个思想建立的表为哈希表。

对不同的关键字可能得到同一哈希地址,这种现象称为冲突。具有相同函数值的关键字对该哈希函数来说称做同义词。在一般情况下,冲突只能尽可能地少,而不能完全避免。因为,哈希函数是从关键字集合到地址集合的映像。通常,关键字集合比较大,它的元素包括所有可能的关键字,而地址集合的元素仅为哈希表中的地址值。

若对一个关键字集合中的任一个关键字,经过哈希函数映像到地址集合中任何一个地址的概率是相等的,则称此类哈希函数为均匀的哈希函数。换句话说,就是使关键字经过哈希函数得到一个“随机的地址”,以便使一组关键字的哈希地址均匀分布在整个地址区间中,从而减少冲突。

1.2、处理冲突的方法

1.2.1、开放寻址法(open addressing)

在开放寻址法中,所有的元素都存放在哈希表中。当查找某个元素时,要系统地检查所有的表项,直到找到所需的元素,或者最终查明该元素不在表中。

1.2.1.1、线性探测

线性探测采用的哈希函数为:
h(k,i) = (h(k) + i)mod m,i = 0,1,…,m - 1
其中 m 为哈希表表长。

1.2.1.2、二次探测

二次探测采用的哈希函数为:
h(k,i) = (h(k) + a * i + b * i * i)mod m,i = 0,1,…,m - 1
其中 m 为哈希表表长。

1.2.2、链接法(chaining)

将所有关键字为同义词的记录存储在同一线性表中。STL的哈希表采用的便是这种做法。

1.3 具体代码示例

1.3.1、哈希表节点的定义

struct _Hash_node_base
{
  _Hash_node_base* _M_nxt;

  _Hash_node_base() noexcept : _M_nxt() { }

  _Hash_node_base(_Hash_node_base* __next) noexcept : _M_nxt(__next) { }
};

template <typename _Value>
struct _Hash_node_value_base : _Hash_node_base
{
  typedef _Value value_type;
  ...
};

/**
*  Primary template struct _Hash_node.
*/
template <typename _Value, bool _Cache_hash_code>
struct _Hash_node;

/**
*  Specialization for nodes with caches, struct _Hash_node.
*
*  Base class is __detail::_Hash_node_value_base.
*/
template <typename _Value>
struct _Hash_node<_Value, true> : _Hash_node_value_base<_Value>
{
  std::size_t _M_hash_code;

  _Hash_node *
  _M_next() const noexcept
  {
    return static_cast<_Hash_node *>(this->_M_nxt);
  }
};

/**
*  Specialization for nodes without caches, struct _Hash_node.
*
*  Base class is __detail::_Hash_node_value_base.
*/
template <typename _Value>
struct _Hash_node<_Value, false> : _Hash_node_value_base<_Value>
{
  _Hash_node *
  _M_next() const noexcept
  {
    return static_cast<_Hash_node *>(this->_M_nxt);
  }
};

1.3.2、哈希表的构造和内存管理

template <typename _Key, typename _Value, typename _Alloc,
          typename _ExtractKey, typename _Equal,
          typename _H1, typename _H2, typename _Hash,
          typename _RehashPolicy, typename _Traits>
class _Hashtable
    : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
                                       _H1, _H2, _Hash, _Traits>,
      public __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
                                 _H1, _H2, _Hash, _RehashPolicy, _Traits>,
      public __detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
                               _H1, _H2, _Hash, _RehashPolicy, _Traits>,
      public __detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
                                    _H1, _H2, _Hash, _RehashPolicy, _Traits>,
      public __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
                                 _H1, _H2, _Hash, _RehashPolicy, _Traits>,
      private __detail::_Hashtable_alloc<
          __alloc_rebind<_Alloc,
                         __detail::_Hash_node<_Value,
                                              _Traits::__hash_cached::value>>>
{
  __bucket_type *_M_buckets = &_M_single_bucket;
  size_type _M_bucket_count = 1;
  __node_base _M_before_begin;
  size_type _M_element_count = 0;
  _RehashPolicy _M_rehash_policy;
  __bucket_type _M_single_bucket = nullptr;
  ...
};

template <typename _Key, typename _Value,
          typename _Alloc, typename _ExtractKey, typename _Equal,
          typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
          typename _Traits>
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
           _H1, _H2, _Hash, _RehashPolicy, _Traits>::
    _Hashtable(size_type __bucket_hint,
               const _H1 &__h1, const _H2 &__h2, const _Hash &__h,
               const _Equal &__eq, const _ExtractKey &__exk,
               const allocator_type &__a)
    : _Hashtable(__h1, __h2, __h, __eq, __exk, __a)
{
  auto __bkt = _M_rehash_policy._M_next_bkt(__bucket_hint);
  if (__bkt > _M_bucket_count)
  {
    _M_buckets = _M_allocate_buckets(__bkt);
    _M_bucket_count = __bkt;
  }
}

// Return a bucket size no smaller than n (as long as n is not above the
// highest power of 2).
std::size_t _M_next_bkt(std::size_t __n) noexcept
{
  const auto __max_width = std::min<size_t>(sizeof(size_t), 8);
  const auto __max_bkt = size_t(1) << (__max_width * __CHAR_BIT__ - 1);
  std::size_t __res = __clp2(__n);

  if (__res == __n)
    __res <<= 1;

  if (__res == 0)
    __res = __max_bkt;

  if (__res == __max_bkt)
    // Set next resize to the max value so that we never try to rehash again
    // as we already reach the biggest possible bucket number.
    // Note that it might result in max_load_factor not being respected.
    _M_next_resize = std::size_t(-1);
  else
    _M_next_resize = __builtin_ceil(__res * (long double)_M_max_load_factor);

  return __res;
}

__bucket_type * _M_allocate_buckets(size_type __n)
{
  if (__builtin_expect(__n == 1, false))
  {
    _M_single_bucket = nullptr;
    return &_M_single_bucket;
  }

  return __hashtable_alloc::_M_allocate_buckets(__n);
}

template <typename _NodeAlloc>
typename _Hashtable_alloc<_NodeAlloc>::__bucket_type *
_Hashtable_alloc<_NodeAlloc>::_M_allocate_buckets(std::size_t __n)
{
  __bucket_alloc_type __alloc(_M_node_allocator());

  auto __ptr = __bucket_alloc_traits::allocate(__alloc, __n);
  __bucket_type *__p = std::__to_address(__ptr);
  __builtin_memset(__p, 0, __n * sizeof(__bucket_type));
  return __p;
}
// Insert v if no element with its key is already present.
template <typename _Key, typename _Value,
          typename _Alloc, typename _ExtractKey, typename _Equal,
          typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
          typename _Traits>
template <typename _Arg, typename _NodeGenerator>
auto _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
                _H1, _H2, _Hash, _RehashPolicy, _Traits>::
    _M_insert(_Arg &&__v, const _NodeGenerator &__node_gen, true_type,
              size_type __n_elt)
        -> pair<iterator, bool>
{
  const key_type &__k = this->_M_extract()(__v);
  __hash_code __code = this->_M_hash_code(__k);
  size_type __bkt = _M_bucket_index(__k, __code);

  __node_type *__n = _M_find_node(__bkt, __k, __code);
  if (__n)
    return std::make_pair(iterator(__n), false);

  __n = __node_gen(std::forward<_Arg>(__v));
  return {_M_insert_unique_node(__bkt, __code, __n, __n_elt), true};
}

__node_type *
_M_find_node(size_type __bkt, const key_type &__key,
             __hash_code __c) const
{
  __node_base *__before_n = _M_find_before_node(__bkt, __key, __c);
  if (__before_n)
    return static_cast<__node_type *>(__before_n->_M_nxt);
  return nullptr;
}

template <typename _Key, typename _Value,
          typename _Alloc, typename _ExtractKey, typename _Equal,
          typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
          typename _Traits>
auto _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
                _H1, _H2, _Hash, _RehashPolicy, _Traits>::
    _M_insert_unique_node(size_type __bkt, __hash_code __code,
                          __node_type *__node, size_type __n_elt)
        -> iterator
{
  const __rehash_state &__saved_state = _M_rehash_policy._M_state();
  std::pair<bool, std::size_t> __do_rehash = 
  _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count, __n_elt);

  __try
  {
    if (__do_rehash.first)
    {
      _M_rehash(__do_rehash.second, __saved_state);
      __bkt = _M_bucket_index(this->_M_extract()(__node->_M_v()), __code);
    }

    this->_M_store_code(__node, __code);

    // Always insert at the beginning of the bucket.
    _M_insert_bucket_begin(__bkt, __node);
    ++_M_element_count;
    return iterator(__node);
  }
  __catch(...)
  {
    this->_M_deallocate_node(__node);
    __throw_exception_again;
  }
}
STL源码剖析——散列表hashtable 与前面介绍的底层机制基于红黑树RB-Tree容器不同,本文介绍的是哈希表hash table,在SGI STL中,给出了底层机制基于哈希表的相关容器的介绍,这里介绍hash table的源码剖析,有利于对后面容器的理解。 阅读详情

相关推荐

Docker 安装 CentOS7

本文主要介绍了docker 下安装centos7。访问 CentOS 镜像库地址:https://hub.docker.com/_/centos?tab=tags&page=1。可以通过 Sort by 查看其他版本的 CentOS 。默认是最新版本 centos:latest 。 使用以下命令来查看是否已安装了 centos7: 启动centos容器,并把docker上centos的22端口映射到本机60001端口(端口号可以自己指定) 3、进入到Centos容器 4.安装ssh服务和网络必须软件

折腾java的博客 1万+

C++ STL --哈希表

在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到$log_2N$,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,本文中只对和进行介绍,和学生可查看文档介绍。是存储键值对的关联式容器,其允许通过keys。

cat_fish_rain的博客 3411

【论文阅读笔记】One Shot 3D Photography

论文主页:https://facebookresearch.github.io/one_shot_3d_photography/ 代码地址:https://github.com/facebookresearch/one_shot_3d_photography   该3D照片方向,由于刚入门,所以会倾向于翻译文章。每个操作上的选择,及为什么不进行另外操作的原因都如数放上。 论文小结   本作的工作是从单张RGB图片生成一个移动视角的3D照片。   其管道大致如下: 由RGB图像通过单目深度估计网络估计深度图

时光机 °的博客 1839

哈希表原理及stl中的简单用法

哈希表又叫做散列表,提供了快速插入操作和查找操作,无论数据多少,插入和查找操作的时间复杂度都是O(1),哈希表的查找速度非常快。所以在很多程序中都有使用哈希表,如拼音检查器。哈希表是基于数据的一种数据结构,数组的特点是根据下标查找时速度快,但内存扩容的成本很高,因此哈希表满时,性能下降严重。哈希表的一个重要概念是将键或关键字转换为数组下标,在哈希表中,这个过程由哈希函数完成,但有的键或关键字可以直接转换为数组的下标而不需要哈希函数。如在大学期间,每个人的学号是独特的,因此学号作为键或关键字。

m0_62467316的博客 673

Qwen-Image-2.1本地图像生成整合包:8G显存一键部署指南

文生图模型是AI视觉创作的核心技术,其本地化部署需兼顾模型能力、硬件适配与工程易用性。Qwen-Image-2.1作为支持文本生成、图像编辑与多图参考的统一架构模型,突破了传统Diffusion模型在消费级显卡上的运行瓶颈。通过AWQ量化、vLLM推理优化与ComfyUI节点重构,实现8G显存稳定运行,显著降低CUDA环境配置复杂度。该方案面向设计师、教师与普通用户,提供开箱即用的本地AI图像工作流,适用于电商主图迭代、教学配图生成与IP角色延展等真实场景,让高质量图像生成真正走出云端,落地桌面。

weixin_29082143的博客 398

OpenWrt kmod内核不兼容?手把手教你用脚本强制安装驱动模块(附国内镜像源)

本文详细解决了OpenWrt kmod内核不兼容问题,提供智能脚本强制安装驱动模块的方法,并附有国内镜像源加速配置。通过自动化脚本修改内核MD5校验值,确保kmod包正常安装,同时推荐多个国内镜像源以提升下载速度。

weixin_42549451的博客 542

<stl系列>哈希表详解,掌握stl容器从现在开始

哈喽,小伙伴们大家好,今天我们来学习几个新的容器。这几个容器存储和查找数据效率是非常高的,能达到非常理想的O(1),这主要因为它们采用了哈希的思想。那么事不宜迟,我们一起来看看吧。一、unordered系列关联式容器c++中容器分为序列式容器和关联式容器。所谓序列容器,就是以线性排列来排列某种类型的数据,比如vect,list,dequene。序列式容器通常只是单纯的用来存储数据。

weixin_59371851的博客 2236

2023东华大学机试OJ进阶版1-81题

先排序。每次找两个最小的数,把其中一个置为两数之和,并累加这个和值,另一个数置为-1,然后再排序,重复操作,直到只剩下一个数。105。

m0_49263811的博客 2796

算法之路:c++STL 哈希表

是一个模板类,用于定义哈希表,其中Key是键类型,T是值类型,Hash是哈希函数的类型,Pred是键比较函数的类型,Alloc是分配器类型。在定义时,可以省略其中的一些参数,因为它们都有默认值。

wzc200406的博客 709

飞书云文档API操作详细介绍

开通云文档API、python程序调用飞书api、飞书获取app_id和app_secret、

Brother_ning的博客 1万+

第一篇:常见CAN错误帧及其排查方向

在本文中主要阐述各类错误帧的形成原因以及在使用同星硬件设备出现错误帧时,应当 如何进行排查并消除错误帧。在 CAN 总线通信中,通常遇见的错误帧主要为以下五种,其 分别是:bit error、ACK error、stuff error、CRC error 以及 form error。

u013089100的博客 1745

哈希表及其在STL中的实现

最近刷题遇到哈希表(散列表)的应用,对这方面不太了解的话用起来还是有点茫,首先总结一下什么是哈希表。 散列表查找(哈希表)概述 一般用来存储数据的顺序表我们是比较熟悉的,存储时依次存入,但是要查找顺序表中某个关键字的记录,就需要从表头依次查找。比如日常用的数组或链表中,要知道某个数据在数组或链表中的下表或者第几个位置需要遍历数组找到该数据,才能得到查询结果,这样查找的时间复杂度较高。因此能不能能不遍历直接查找呢?当然是可以的,这就用到我们所要讲的散列技术了。 散列技术是...

qq_39010744的博客 1992

TGAT Python:图神经网络中的动态图算法

其中,图神经网络中的动态图算法成为了研究的热点之一。在模型的 forward 方法中,通过循环调用 TGATLayer 实例来进行图神经网络的前向传播。最终,模型返回图中节点的表示向量 output,可以用于图分类等任务。TGAT 算法的核心思想是在节点之间建立注意力连接,以捕捉节点在不同时间步之间的关系。TGAT 算法通过引入注意力机制和时间信息,从而有效地对动态图数据进行建模和分析。通过以上 Python 代码,我们可以方便地构建和训练 TGAT 模型,并在图分类任务中应用它。

2301_79366435的博客 1827

STL关联式容器之散列表——hashtable

以散列表为基础的关联式容器深受人们的喜欢,虽然它们目前还不是STL的标准,但是SGI的STL已经包含了这些内容。 前面学习的关联式容器都是基于红黑树这样一种二叉搜索树,也正因为这样,如果要求搜索时间具有对数平均时间,那么需要数据有足够的随机性。而哈希表这种数据结构,在插入、删除、搜索等操作也具有“常数平均时间”的表现,而这种表现是以统计为基础的,不需要依赖数据输入的随机性。

handsomeKyne 944

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Day11 散列表 哈希表 STL

散列表 就是把大范围的定义域变成很小的值域 通过k=(x%N+N)%N来实现 N一般取质数,因为这样冲突概率最小 传送门 #include<iostream> #include<cstring> using namespace std; const int N=100003; int h[N],ne[N],idx,val[N]; void insert(int x){ ...

离开那天的博客 261

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

STL中的哈希表 hash table

哈希表在STL的map库中,用到的两个主要函数为map.count()和map.insert(),分别是查找和插入操作。代码样例如下: 1 include <map> 2 3 map<int, int> hashtable; 4 if(!hashtable.count(key)) { 5 hashtable.insert(pair<i...

Yaphets__2015的博客 277

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

一文看懂哈希表并学会使用C++ STL 中的哈希表

    最近在刷题以及做编程练习的作业时经常会用到哈希表,碰到一些想用的函数时每次都看别人的博客,现结合别人的博客对哈希表做个总结。 本篇哈希表的作用如何使用STL库中的哈希表STL中哈希表的常用函数 哈希表的作用 如何使用STL库中的哈希表 STL中哈希表的常用函数 ...

Peealy的博客 8万+

C++STL哈希表

我最近开了几个专栏,诚信互三!====> |||::刷题教程来自网站。|||====> |||::记录我学习C++的经历,看完你一定会有收获。|||====> |||::记录我学习Linux的经历,看完你一定会有收获。|||====> |||::记录我复习C#的经历,,不定期更新。|||====> |||::记录我学习计算机网络,看完你一定会有收获。|||

reyas的博客 1270

C++STL之哈希表

C++ STL中,哈希表对应的容器是unordered_map(since C++ 11)。根据 C++ 11 标准的推荐,用unordered_map代替hash_map。 哈希表 先来回顾一下数据结构中哈希表相关的知识。 哈希表是根据关键码值(key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数。 哈希表的一个重要问题就是如何解决映射冲突的问题。常用的有两种:开放地址法和链地址法。 STL中...

lanshi00的博客 8748

【数据结构】—— 哈希表/STL使用技巧

【数据结构】—— 哈希表/STL使用技巧

forever_bryant的博客 939

一篇足矣,带你吃透STL源码中hash table(哈希表)与关联式容器hash_set、hash_map

前面介绍的二叉搜索树和平衡二叉搜索树。二叉搜索树具有对数平均时间的表现,但这样的表现构造在一个假设上:输入数据有足够的随机性 文本要介绍一种名为hash table(哈希表/散列表)的数据结构,这种结构在插入、删除、搜索等操作上也具有“常数平均时间”的表现,而且这种表现是以统计为基础,不需依赖输入元素的随机性 哈希表可以在本人的数据结构文章中查看,文本就不再详细介绍了:https://blog...

董哥的黑板报 3499

哈希表原理以及STL使用示例

让我们来就哈希表做一个最简单的介绍哈希表是一个通过key-value对(键-值对)实现快速查找数据的简单数据结构key是我们将对象插入哈希表时提取的特征,value则是我们需要储存的值。如果把哈希表理解成一座公寓,那么key在我看来,可以意译为“钥匙”,一把钥匙只能打开一扇门,所以一个key值只能对应一个value。使用哈希表,就是利用key值实现对数据(value)的快速查询。接下来,我们来看看哈希表在具体操作中如何使用。

2402_88630322的博客 760

《C++进阶之STL》【哈希表】

默认情况下,STL使用std::hash模板生成哈希值。int id;

jgddfs的博客 164

C++ STL 中哈希表的详细用法

C++ STL 中的和提供了强大的哈希表功能,能够高效地进行插入、删除和查找操作。理解其用法和注意事项,能够帮助我们在实际开发中更好地利用这些容器。

TheJustice_的博客 2649
上一篇: STL之红黑树
下一篇: STL之关联容器
Spring_24
博客等级 码龄12年 46粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Spring_24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值