U8国际 U8国际官方网站 体育APP下载一篇文章让你学会什么是哈希(上)
栏目:U8哈希 发布时间:2025-06-09
  u8,u8国际,u8国际官方网站,u8国际网站,u8国际网址,u8国际链接,u8体育,u8体育官网,u8体育网址,u8注册,u8体育网址,u8官方网站,u8体育APP,u8体育登录,u8体育入口哈希概念哈希在C++中有广泛的应用,它是一种用于快速查找和存储数据的数据结构和算法。以下是一些常见的哈希在C++中的应用:哈希表(Hash Table):哈希表是一种高效的数据结构,用于存储键值

  u8,u8国际,u8国际官方网站,u8国际网站,u8国际网址,u8国际链接,u8体育,u8体育官网,u8体育网址,u8注册,u8体育网址,u8官方网站,u8体育APP,u8体育登录,u8体育入口哈希概念哈希在C++中有广泛的应用,它是一种用于快速查找和存储数据的数据结构和算法。以下是一些常见的哈希在C++中的应用:哈希表(Hash Table):哈希表是一种高效的数据结构,用于存储键值对。在C++中,std::unordered_map 和 std::unordered_set 是标准库提供的哈希表实现。

  哈希在C++中有广泛的应用,它是一种用于快速查找和存储数据的数据结构和算法。以下是一些常见的哈希在C++中的应用:

  哈希表(Hash Table):哈希表是一种高效的数据结构,用于存储键值对。在C++中,

  是标准库提供的哈希表实现。这些容器提供了常数时间复杂度的查找、插入和删除操作,使它们非常适用于快速查找和存储数据。

  std::unordered_mapstd::string, int hashMap;

  std::unordered_setint hashSet;

  哈希函数(Hash Function):哈希函数将输入数据映射到一个固定大小的哈希码(哈希值),通常是一个整数。C++标准库提供了多个哈希函数,也允许用户自定义哈希函数。

  哈希集合和哈希映射:除了标准库的哈希表,C++还提供了其他第三方库和实现,例如Google的

  std::unordered_setint uniqueValues;

  uniqueValues.insert(1); // 重复元素将被自动去重

  哈希在密码学中的应用:哈希函数在密码学中用于存储和验证密码,常用的密码哈希函数包括SHA-256和bcrypt等。

  缓存实现:哈希表可用于实现缓存,通过将键值对存储在哈希表中,可以在常数时间内查找缓存中的数据。

  数据唯一性检查:哈希函数和哈希表可用于检查数据的唯一性,确保不会存储重复的数据。

  顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log_2 N),搜索的效率取决于搜索过程中元素的比较次数。

  如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

  根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放

  对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功

  该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)

  用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快,但随之而来的问题是,取余数时如果两个数余数相同该怎么办?

  在散列表(Hash Table)形式的存储中,哈希冲突指的是当不同的键(或数据)经过哈希函数计算后,它们得到了相同的哈希值,并且尝试存储到相同的散列表桶(或哈希表位置)时发生的情况。由于哈希函数将输入数据映射到有限数量的桶中,而输入数据的数量可能远远大于桶的数量,因此哈希冲突是在散列表中常见的问题。

  链地址法(Chaining):在这种方法中,每个桶都存储一个链表、数组或其他数据结构,用于存储映射到相同哈希值的多个元素。当哈希冲突发生时,新元素被添加到桶内的链表或数组中,而不会覆盖已存在的元素。

  开放寻址法(Open Addressing):在这种方法中,当发生哈希冲突时,算法会寻找散列表中的下一个可用桶,并将数据插入到该桶中,直到找到一个空闲桶。这个过程通常涉及一系列的探测方法,如线性探测、二次探测等。

  好的哈希函数:选择合适的哈希函数可以减少哈希冲突的发生。好的哈希函数应该尽量均匀地将数据映射到桶中,从而降低冲突的概率。

  处理哈希冲突是设计和实现散列表时需要考虑的重要问题,不同的应用场景可能需要选择不同的冲突解决策略。合理的冲突处理方法可以提高散列表的性能和效率。

  哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间

  “直接定址法”(Direct Addressing)是一种简单且有效的散列(哈希)技术,通常用于解决键-值对的存储和检索问题。在直接定址法中,每个可能的键都对应着一个桶(或槽位),而这些桶按照键的范围来分配,通常作为一个数组来实现。

  这种方法的核心思想是,每个键都被直接映射到一个特定的桶,因此在理想情况下,没有哈希冲突,因为每个键都有唯一的桶索引。

  :这种方法需要分配足够大的存储空间以容纳所有可能的键,因此空间复杂度取决于键的范围大小。如果键的范围很大,将导致高内存消耗。

  :直接定址法通常不需要冲突解决策略,因为每个键都有唯一的桶索引。这使得存储和检索操作都是O(1)的常数时间复杂度,非常高效。

  :直接定址法通常在键的范围相对较小且连续的情况下有效。如果键的范围非常大或不连续,这种方法可能不太适用,因为需要分配大量的存储空间。

  :一个常见的示例是使用直接定址法来实现一个包含整数键的数据结构,例如计数器。如果你有一个计数器,需要跟踪大量整数值的出现次数,你可以使用一个数组,其中数组的索引是整数值,数组的值是该整数值出现的次数。

  const int MAX_RANGE = 1000; // 假设整数范围在0到999之间

  总之,直接定址法是一种简单但有效的散列方法,适用于键的范围相对较小且连续的情况。它提供了常数时间复杂度的存储和检索操作,但需要注意键的范围和内存消耗,适合查找比较小且连续的情况。

  “除留余数法”(Division Method)是一种常见的散列(哈希)技术,用于将键映射到散列表的桶(槽位)或数组索引中。它的基本思想是,通过对键除以一个合适的数并取余数来计算哈希值,然后将哈希值作为桶的索引。这个余数应该是一个较小的正整数,通常是质数,以确保较好的分布性。

  :选择一个合适的除数M对于除留余数法至关重要。一个好的选择可以确保较好的哈希分布,避免哈希冲突。通常,选择质数可以帮助减少冲突的可能性。

  :为了获得良好的哈希分布,键应该均匀地分布在整个键空间。如果键分布不均匀,可能会导致某些桶过度拥挤,而其他桶几乎没有数据。

  :除留余数法通常要求键是正整数。如果需要哈希负数,可以考虑采用一些调整方法,如将负数转换为正数。

  :虽然除留余数法可以减少冲突的可能性,但仍然可能发生冲突。在实际应用中,通常需要使用冲突解决策略,如**链接法(Chaining)或开放寻址法(Open Addressing)**来处理冲突。

  “平方取中法”(Mid-Square Method)是一种简单的伪随机数生成方法,通常用于产生伪随机整数序列。它的基本思想是通过对一个整数进行平方运算,然后取中间的数字作为下一个整数,不断迭代生成伪随机数。尽管它简单,但它的质量通常不如更复杂的伪随机数生成算法。

  假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址

  再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址

  这个方法的核心思想是通过反复平方和取中间数字的操作来生成伪随机数。然而,平方取中法的质量和均匀性通常不如更复杂的伪随机数生成算法,因此它主要用于教学或一些简单的模拟问题。

  平方取中法的性能和均匀性取决于初始种子的选择和提取的位数。如果选择的种子不好或者提取的位数不适当,可能会导致周期性或不均匀的伪随机数序列。因此,在实际应用中,通常会使用更先进和更可靠的伪随机数生成器,如线性同余法(Linear Congruential Generator)或梅森旋转法(Mersenne Twister)等,以获得更高质量的伪随机数,平方取中法比较适合不知道关键字的分布,而位数又不是很大的情况。

  “折叠法”(Folding Method)是一种散列(哈希)技术,通常用于将大整数或长字符串键映射到较小的哈希值,以便将它们存储在散列表或哈希表中。折叠法的基本思想是将输入键分割成固定长度的部分,然后将这些部分相加或进行其他数学操作以生成哈希值。

  选择一个适当的分割大小,通常是一个正整数。这个大小可以根据应用的需要选择,通常是使输入键能够均匀分割的值。

  将输入键分割成固定大小的部分(分割块),可以是连续的字符、数字或其他适当的单位。如果输入是数字,通常将它们分割成相等数量的部分。

  对这些部分执行数学操作,通常是求和。可以选择不同的数学操作,如加法、位操作等,以根据具体情况来生成哈希值。

  假设输入键是1234567890,我们选择分割块大小为3,然后将这个整数分割成 1, 234, 567, 和 890。接下来,我们对这些部分进行求和操作,得到哈希值:1 + 234 + 567 + 890 = 1692。这个哈希值可以用于存储和检索数据。

  折叠法的性能和均匀性取决于分割大小和数学操作的选择。如果选择得当,它可以提供较好的哈希分布,但也需要谨慎选择参数以避免潜在的问题,如冲突或不均匀的哈希分布,折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况。

  选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。

  设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。

  假设要存储某家公司员工登记表,如果用手机号作为关键字,那么极有可能前7位都是 相同的,那么我们可以选择后面的四位作为散列地址,如果这样的抽取工作还容易出现 冲突,还可以对抽取出来的数字进行反转(如1234改成4321)、右环位移(如1234改成4123)、左环移位、前两数与后两数叠加(如1234改成12+34=46)等方法。

  数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀的情况

  闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去

  比如上面概念中提到的的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,hashAddr为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。

  线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

  如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素

  采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。

  EMPTY此位置空, EXIST此位置已经有元素, DELETE元素已经删除

  定义模板结构体HashData,用于表示哈希表中的数据项。它包含两个主要成员:

  这种结构的设计是为了在哈希表中存储键值对并跟踪每个槽位的状态。状态_state的存在使得哈希表可以处理删除操作,而不仅仅是插入和查找。

  定义通用的哈希函数模板HashFunc,它可以用于任何类型的键K。该哈希函数的实现非常简单,它将输入键key直接转换为size_t类型并返回。

  需要注意的是,它可能不适用于所有类型的键,特别是对于自定义的数据类型,可能需要更复杂的哈希函数来确保良好的哈希性能和均匀性。

  定义特化版本的哈希函数HashFunc,用于处理字符串类型(string)的键。这个哈希函数将字符串的每个字符转换成对应的整数值,并将它们组合在一起生成一个哈希值。

  这个哈希函数的特点是简单而有效,它将字符串中的每个字符都考虑到了哈希值中,并且使用质数131来进行混合(参考STL源码中的实现),以增加哈希的均匀性。这种方法在许多情况下可以产生良好的哈希结果。

  定义模板类HashTable,用于实现哈希表数据结构。这个哈希表可以存储键值对,其中键的类型为K,值的类型为V,并且可以选择性地指定哈希函数的类型,默认使用HashFuncK作为哈希函数。

  ),表示哈希表的存储空间。每个元素对应哈希表的一个槽位,可以存储一个键值对。哈希表的大小由向量的大小决定。

  :这是一个记录有效数据项数量的计数器,表示当前哈希表中存储了多少个有效的键值对。在插入和删除操作时,会更新

  ,可以选择性地指定自定义的哈希函数。如果未提供自定义的哈希函数类型,将使用默认的

  插入函数需要考虑扩容问题,在散列表存储形式的扩容中,我们需要考虑负载因子问题

  α是散列表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,表明填入表中的元素越多,产生冲突的可能性就越大;反之,α越小,表明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子α的函数,只是不同处理冲突的方法有不同的函数。

  对于开放定址法,负载因子是特别重要因素,应严格限制在0.7-0.8以下。超过0.8,查表时的CPU缓存不命中(cache missing)按照指数曲线上升。因此,一些采用开放定址法的hash库,如Java的系统库限制了荷载因子为0.75,超过此值将resize散列表。

  Insert方法的实现,用于向哈希表中插入新的键值对。以下是该方法的主要步骤和逻辑:

  方法检查是否已经存在具有相同键的数据项。如果存在相同的键,插入失败,直接返回

  接下来,检查哈希表的负载因子(load factor)。负载因子是当前存储的有效数据项数量

  的比值。如果负载因子超过了指定的阈值(7/10),则表示哈希表负载过重,需要进行扩容。扩容的目的是保持哈希表的性能和均匀性。

  ,其大小为当前哈希表的两倍(或初始化大小为10,如果当前哈希表为空)。然后,将旧哈希表

  Find方法的实现,用于查找指定键的数据项并返回对应的HashDataK, V指针。以下是该方法的主要步骤和逻辑: