精华内容
下载资源
问答
  • 这意味着我们需要时刻留意哈希表的尺寸以及当前表中已有的元素数量。因为一旦哈希表中有太多元素,也将很难找到可用的位置来存放我们新插入的元素,因此这里我们需要引入一个重要的术语,负载系数(Load Factor)负载...

    开放寻址是其中一种缓解散列冲突的编程技术,当使用开放寻址作为冲突解决技术时,键值对存储在表(数组)中,而不是像单独链表那样的数据结构中。这意味着我们需要时刻留意哈希表的尺寸以及当前表中已有的元素数量。因为一旦哈希表中有太多元素,也将很难找到可用的位置来存放我们新插入的元素,因此这里我们需要引入一个重要的术语,负载系数(Load Factor)

    负载系数

    4e4286bc17493b8ae6f14ae425984721.png


    其实就是表中已有元素个数和表尺寸的比例,我们要密切关注这个系数的是因为哈希表的O(1)恒定时间行为假设负载因子k保持一定的固定值,这意味着一旦k>阈值,我们就需要增加表的大小(理想情况下是指数增长,例如,两倍)

    517b898d56cdeb4e6334a45e1eec71dd.png


    在上图中,你会看到有两种缓解冲突的方法,即单独链表和线性探测(Linear Probing),在开放寻址(线性探测)技术看来,一旦达到某个阀值,它的时间复杂度就会呈现指数级恶化的趋势

    当我们想要将键值对插入哈希表时,我们对键进行哈希处理并获得该键值对所属位置的原始位置。如果我们的键被散列到的位置被占用(此时出现了冲突),对于开放寻址来说,同一个位置中不允许有两个键的,这不是数组的工作方式,我们要做的是使用一个探测序列函数(Probing Seque Function) 这里简称p(x),因为我们已从散列函数获取了冲突点的所在位置,现在我们使用p(x)进行探测直到在沿途发现一个空闲的位置为止

    探测函数

    您可以提出无限数量的探测序列,这里仅提及一些常见的探测函数:

    • 线性探测(Linear Probing):p(x)= kx + b其中a,b是常数
    • 二次探测(Quaratic Probing):p(x)= ax ^ 2 + bx + c,其中a,b,c是常数
    • 双重散列(Double Hashing):p(k,x)= x * h(k),其中h(k)是辅助s散列函数
    • 伪随机数发生器(Pseudo Random Number Generator): p(k,x)= x*RNG(h(k),x)其中RNG是以H(k)作为种子的随机数生成器函数

    本篇仅介绍线性探测函数进行线性探测,因此给定输入参数x,当我们进行探测时,我们通常会将变量x初始化为0或1作为一个起点,如果我们找不到空闲的位置,会依次将x增加1,对以上所有这些探测函数都是一样的

    开放寻址的通用算法

    接下来,这是一个通用的开放寻址插入算法,假设我们有一个表的尺寸为n,开放寻址算法首先会初始化变量x=1,因为x是一个变量,我们要用它来探测,每当我们未能到达闲置的位置时,都需要递增x,然后我们通过散列函数获得keyHash,而实际上我们首先要查看表的索引,当表索引被占用意味着它不为空,那么新索引就是我们散列的最初位置(keyHash所指向的起始索引)加上探测函数的总和再于表尺寸N取模运算得到整数,由于我们总是回到表里,在循环中要递增x。下一次当我们在不同的位置探测时,在while循环中,最终我们会找到一个空闲的位置

    x=1keyHash=h(k)index=keyHashwhile table[index]!=NULL:      index=(keyHash+p(k,x)) mod N      x=x+1insert(k,v,index) 

    死循环地狱(Chaos with Cycle)

    由于我们知道负载系数被控制在一定的范围内,所以这里有个问题,就是开放寻址中的探测函数存在缺陷--死循环地狱,以表尺寸N为模的大多数随机选择的探测序列将产生比表大小N更短的循环。当您尝试插入一个键-值对并且循环中的所有存储桶都被占用时,这将成为灾难性问题,因为您将陷入无限循环,这在一些老外谈及哈希表的相关文章中有一个非常有趣的昵称叫死循环地狱(Chaos with Cycle)

    为了生动说明什么叫死循环地狱,我们这里看一个例子,这里有一个尺寸为12的哈希表,并且使用开放寻址插入了一些键值对,,该哈希表已经部分填充。 占用的单元格填充有键值对(Ki,Vi)和带有空令牌Φ的空单元格,如下图所示

    7002536b795afe4966059f7707786b97.png

    假设我们使用探测序列函数p(x)=4x,并且在表中插入一个新的键值对,并且该键值对的散列值为8,即h(x)=8这意味着我们会在索引8的位置插入该键值对,但是该位置已被占用,因为这里已经有简直对(k5,v5),所以我们该怎么办呢?接下来,我们需要进行探测,所以我们计算: index=h(k)+p(1)=8+4 mod 12=0

    此时,如下图,此时探测函数会跳转到索引为0的位置,糟糕的是索引1的位置也被占用了,因为(k1,v1)已经存在.

    a78a462e9e581b904abb5dc9cd3cc92b.png
    • 当x=2时,即index=h(k)+p(2)=(8+8) mod 12=4,探测函数会跳跃到索引4的位置,同样这里也是被占用的,如此类推
    • 当x=3时,即index=h(k)+p(3)=(8+12) mod 12=8,p(x)跳跃到索引8的位置,该位置被占用
    • 当x=4时,即index=h(k)+p(4)=(8+16) mod 12=0,p(x)跳跃到索引0的位置,该位置被占用
    • 当x=5时,即index=h(k)+p(5)=(8+20) mod 12=4,p(x)跳跃到索引4的位置,该位置被占用
      .....

    这样尽管我们具有探测函数,但这种特定的情况下它一直在一个死循环里面一直做一些毫无意义的事情。

    由这个例子我们可知探测函数存在缺陷,他们产生的周期短于表的尺寸,因此,我们要如何处理产生小于表大小的周期的探测功能?一般来说,一致的看法是我们不处理这个问题,相反,我们通过将探测函数的范围限制在那些产生长度为N的循环的函数上来避免这个问题,我们选择的那些产生的周期正好为N的探测函数,并且这些探测函数确实存在。

    线性探测、二次探测和双重散列等技术都受到死循环地狱问题的影响,这就是为什么与这些方法一起使用的探测函数非常特殊的原因。这是一个很大的话题,将是接下来几篇文章会重点讲述这些,我们目前需要做的是重新定义非常具体的探测函数,这些函数会产生一个循环长度为表尺寸N,并且避免无法插入元素或陷入无限循环

    注意,开放寻址对使用的哈希函数和探测函数非常敏感。如果使用单独的链接作为冲突解决方法,则不必担心此问题。

    小结

    我们本篇用一个反例生动地介绍了开放寻址插入算法的底层是由探测函数和散列函数相互作用的结果,同时我们也介绍了一些探测函数的固有缺陷,就是死循环地狱,下一篇我们会详细讨论线性探测函数的原理,敬请期待。


    链接:https://www.jianshu.com/p/b8c47701dd07

    展开全文
  • C++哈希表的实现

    2021-01-23 21:22:15
    C++哈希表的实现前言源码如下: 前言 本篇文章为笔者的读书笔记,未经允许请勿转载。如果对你有帮助记得点个赞(●’◡’●) 本文主要讲的哈希表的创建和使用, 源码如下: main #include <iostream> #...

    C++哈希表的实现


    前言

    本篇文章为笔者的读书笔记,未经允许请勿转载。如果对你有帮助记得点个赞(●’◡’●)
    本文主要讲的哈希表的创建和使用。哈希表的存储方式是不可逆,存储的value是经过哈希算法得到,所以只是获得存储中的value是毫无意义的,它只是密码经过哈希算法后得到的数值。


    源码如下:

    main

    #include <iostream>
    #include<string>
    #include"HashTable.hpp"
    using namespace std;
    void test()
    {
        //创建一个有15个节点的哈希表;
        HashTable<string> ht(15);
        
        bool is;
        cout << boolalpha;
        is = ht.insert(11, "A");
        cout << is << endl;
        is = ht.insert(22, "B");
        cout << is << endl;
        is = ht.insert(33, "C");
        cout << is << endl;
        is = ht.insert(44, "D");
        cout << is << endl;
        is = ht.insert(45, "D");
        cout << is << endl;
        is = ht.insert(55, "E");
        cout << is << endl;
        is = ht.insert(66, "F");
        cout << is << endl;
        is = ht.insert(77, "G");
        cout << is << endl;
        is = ht.insert(88, "H");
        cout << is << endl;
    
        cout << "-------------------------" << endl;
        ht.out();
        cout << "-------------------------" << endl;
    
        try
        {
            cout << ht.search(44) << endl;
        }
        catch (const char* str)
        {
            cout << str << endl;
        }
        ht.remove(45);
    
        cout << "-------------------------" << endl;
        ht.out();
        cout << "-------------------------" << endl;
    
        HashTable<string> ht1(ht);//调用拷贝构造
        try
        {
            cout << ht1.search(45) << endl;
        }
        catch (const char* str)
        {
            cout << str << endl;
        }
        cout << "-------------------------" << endl;
        ht1.out();
    
    }
    int main()
    {
        test();
        system("pause");
        return 0;
    }
    测试结果:
    true
    true
    true
    true
    true
    true
    true
    true
    true
    -------------------------
    [0]->nil
    [1]->(11:A)->(55:E)->nil
    [2]->nil
    [3]->nil
    [4]->nil
    [5]->nil
    [6]->nil
    [7]->(88:H)->nil
    [8]->(44:D)->nil
    [9]->(45:D)->nil
    [10]->nil
    [11]->(77:G)->nil
    [12]->(66:F)->nil
    [13]->(22:B)->(33:C)->nil
    [14]->nil
    -------------------------
    D//查询的数据
    -------------------------
    [0]->nil
    [1]->(11:A)->(55:E)->nil
    [2]->nil
    [3]->nil
    [4]->nil
    [5]->nil
    [6]->nil
    [7]->(88:H)->nil
    [8]->(44:D)->nil
    [9]->nil
    [10]->nil
    [11]->(77:G)->nil
    [12]->(66:F)->nil
    [13]->(22:B)->(33:C)->nil
    [14]->nil
    -------------------------
    没有这个数据//删除的数据
    -------------------------
    [0]->nil
    [1]->(11:A)->(55:E)->nil
    [2]->nil
    [3]->nil
    [4]->nil
    [5]->nil
    [6]->nil
    [7]->(88:H)->nil
    [8]->(44:D)->nil
    [9]->nil
    [10]->nil
    [11]->(77:G)->nil
    [12]->(66:F)->nil
    [13]->(22:B)->(33:C)->nil
    [14]->nil
    

    HashTable.hpp

    #pragma once
    #include<list>
    #include<utility>//pair
    template <class T>
    class HashTable
    {
    	public:
    		HashTable(int len):length(len),count(0) 
    		{
    			data = new hash_table[length]();//堆内存
    			//hash_table data[length]();栈内存
    
    		}
    		//拷贝构造
    		HashTable(const HashTable& ht):length(ht.length)
    		{
    			this->data = new hash_table[length]();
    			for (int i = 0; i < length; i++)
    			{
    				this->data[i] = ht.data[i];
    			}
    		}
    		~HashTable()
    		{
    			if (data)
    			{
    				delete[] data;
    				data = nullptr;
    			}
    		}
    		//插入数据
    		bool insert(int key, T val)
    		{
    			//通过hash运算再求余的方法插入位置,也可以用其他办法,自由发挥。
    			int base = hash(key) % length;
    			for (auto e:data[base])
    			{
    				if (e.first == key)
    				{
    					return false;
    				}
    			}
    			//数组里面的每一个元素都是一个list,二list里面的每一个元素是pair。
    			data[base].push_back({ key,val });
    			return true;
    		}
    		//查找数据
    		T search(int key)
    		{
    			int base = hash(key) % length;
    			for (auto e : data[base])
    			{
    				if (e.first == key)
    				{
    					return e.second;
    				}
    			}
    			throw "没有这个数据";
    		}
    		//删除数据
    		bool remove(int key)
    		{
    			int base = hash(key) % length;
    			for (auto it = data[base].begin(); it != data[base].end(); it++)
    			{
    				if (it->first == key)
    				{
    					data[base].erase(it);
    					return true;
    				}
    			}
    			return false;
    		}
    		//输出
    		void out()
    		{
    			for (int i = 0; i < length; i++)
    			{
    				std::cout << "[" << i << "]" << "->";
    				for (const auto& e : data[i])
    				{
    					std::cout <<"("<< e.first << ":" << e.second <<")"<< "->";
    				}
    				std::cout << "nil\n";
    			}
    		}
    private:
    	using hash_table = std::list<std::pair<int, T>>;
    	int length = 0;
    	int count = 0;
    	hash_table * data=nullptr;
    	//计算hash值的函数,对于频繁访问的hash函数可以用inline来使用更少的栈内存
    	inline int hash(int key)
    	{	
    		return key^10;
    	}
    };
    

    哈希表的实现原理不懂的可以参考一下
    可视化哈希表

    展开全文
  • C++哈希表的使用

    万次阅读 2019-07-13 22:31:46
    C++中的STL提供了hash_map来实现哈希表功能,在介绍hash_map的使用方法之前,我们先从哈希函数和哈希冲突来了解哈希表。 一、 哈希函数 所谓哈希函数就是从关键字(Key)到值(Value)的映射: Value=H(Key)Value=H...

    C++中的STL提供了hash_map来实现哈希表功能,在介绍hash_map的使用方法之前,我们先从哈希函数和哈希冲突来了解哈希表。

    一、 哈希函数

    所谓哈希函数就是从关键字(Key)到值(Value)的映射:
    Value=H(Key)Value=H(Key)
    值反映了关键字的存储地址。
    1、数字分析法
    选取关键字中的几位数字作为值,一般选取数字分布比较均匀的几位。
    H(k1k2k3k4k5k6k7)=k2k3k5H(k_1 k_2 k_3 k_4 k_5 k_6 k_7)= k_2 k_3 k_5
    2、直接定址法
    选取线性函数作为哈希函数
    H(Key)=aKey+bH(Key)=a*Key+b
    3、折叠法
    将关键字分成数字相同的几段(最后一段数字可以不够),然后相加得到值。
    H(k1k2k3k4k5k6k7)=(k1+k4+k7)(k2+k5)(k3+k6)H(k_1 k_2 k_3 k_4 k_5 k_6 k_7)=( k_1+k_4+k_7)( k_2+k_5)( k_3+k_6)
    k1k2k3k_1 k_2 k_3
    k4k5k6k_4 k_5 k_6
    +k7+ k_7
    4、平方取中法
    将关键字求平方之后选取中间的几位作为值
    5、除留余数法
    关键字除以PP得到的余数作为值,其中P为小于哈希表长度的最大质数。
    H(Key)=KeyMODPH(Key)=Key \quad MOD \quad P
    6、随机数法
    选取一个随机函数作为哈希函数
    H(Key)=random(Key)H(Key)=random(Key)

    二、 哈希冲突

    对于不同的关键字,经过哈希函数的映射可能得到相同的值,也就是发生了冲突,下面介绍几种解决哈希冲突的方法
    1、公共溢出区法
    新建一块内存用来存储发生冲突的数据
    2、再探测法
    当发生冲突时,通过一个函数为关键字寻找新的值:
    Value=(H(Key)+di)MODLValue=(H(Key)+d_i) \quad MOD \quad L
    其中LL为哈希表的长度,根据did_i的取值不同可以细分为以下三种方法:

    • 线性探测法
      di=12L1d_i=1,2,⋯,L-1

    • 平方探测法
      di=12122222d_i=1^2,-1^2,2^2,-2^2,⋯

    • 随机探测法
      这时did_i取的是一组随机数

    3、再哈希法
    当发生冲突时,再进行一次哈希函数,当然这个哈希函数可以和前面的相同,也可以不同
    Value=RH(H(Key))Value=RH(H(Key))
    4、链地址法
    链地址法是一种将数组与链表结合的方法,这也是最常用的一种方法,将相同值的不同关键字存放于一个链表中,不同的链表即不同的值构成了一个数组。

    三、 unordered_map的使用

    1、头文件:

    #include< unordered_map>
    

    2、定义一个哈希表(我们以Key和Value都是int变量为例):

    unordered_map<int,int> Hash;
    

    3、哈希表的建立有下面几种方法:

    Hash[1]=3;
    Hash.insert<make_pair(1,3)>;
    Hash.insert({ {1,3},{2,4} });
    

    4、迭代器

    unordered_map<int,int>::iterator it;
    

    5、利用迭代器访问变量:

    it->first;
    it->second;
    

    6、哈希表的查找:

    it=Hash.find(1);
    

    若找不到,返回的是Hash.end();
    7、修改哈希表:

    Hash[1] = 4;
    

    8、清除哈希表:

    Hash.erase(1);
    Hash.clear();
    

    以上我们介绍了哈希表的几个基本用法,详细的介绍可以访问下方的网址:
    https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/bb982522(v=vs.90)

    展开全文
  • 目录1、概念2、散列表构造方法2.1、直接定址法 ...采用散列技术将记录存储在一块存储空间中,这块连续空间称为散列表或哈希表(Hash-Table)。 2、散列表构造方法 2.1、直接定址法 直接定址法使用

    1、概念

    散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,每个关键字key对应一个存储位置f(key),查找时,根据这个对应的关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。采用散列技术将记录存储在一块存储空间中,这块连续空间称为散列表或哈希表(Hash-Table)。

    2、散列表的构造方法

    2.1、直接定址法

    直接定址法使用下面的公式
    f(key)=a×key+b,b为常数
    比如统计出生年份,那么就可以使用f(key)=key-1990来计算散列地址。
    在这里插入图片描述

    2.2除留取余法

    这种方法是最常用的散列函数构造方法,对于表长为m的散列公式为:
    f(key)=key mod p (p<=m)
    在这里插入图片描述

    2.3数字分析法

    分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址这里使用一个手机号码作为例子,手机号码是一个字符串,一般的说,后面4位是真正的用户号。
    在这里插入图片描述

    2.4折叠法

    把关键词分割成位数相同的几个部分,然后叠加:
    在这里插入图片描述

    2.5平方取中法

    在这里插入图片描述

    3.冲突解决办法

    常用处理冲突的思路:
    1.换个位置:开放地址法
    2.同一个位置的冲突对象组织在一起:链地址法

    3.1开放地址法

    一旦产生了冲突(该地址已有其他元素),就按某种规则去寻找另一空地址,若发生了第i次冲突,试探的下一个地址将增加di,基本公式是:
    hi(key)-(h(key)+di) mod TableSize (1<=i<TableSize)
    这里面di决定了不同的解决冲突方案:线性探测、平方探测、双散列。下面依次介绍各种方法:

    3.1.1线性探测法

    线性探测法以增量序列1,2,…,(TableSize-1)循环试探下一个存储地址。
    【例1】设关键词序列为47,7,29,11,9,84,54,20,30,散列表表长TableSize=11(装填因子α=9/13≈0.69),散列函数为:h(key)=key mod 11.用线性探测法处理冲突,列出依次插入后的散列表,并估算查找性能。
    【解】初步的散列地址如下表所示:

    关键词(key)      47 7 29 11 9 84 54 20 30
    散列地址h(key)	 3 7  7  0  9 7  10 9  8

    可以看出有多个关键词的散列地址发生了冲突,具体见下表

    关键词(key)      47 7 29 11 9 84 54 20 30
    散列地址h(key)   3  7 7  0  9  7 10  9  8
    冲突次数         0  0 1  0  0  3  1  3  6

    具体的哈希表构建过程可以用下面的图表来表示
    在这里插入图片描述

    这里引出一下散列表的的查找性能分析,散列表的查找性能,一般有两种方法
    1.成功平均查找长度(ASLs)
    2.不成功平均查找长度 (ASLu)
    对于上面一题的散列地址冲突次数为

    散列地址h(key)   0  1   2  3  4  5  6  7  8 9 10 11 12
    关键词(key)      11 30    47           7 28 9 84 54 20
    冲突次数         0  6     0            0  1 0  3  1  3

    ASLs: 查找表中关键词的平均查找比较次数(其冲突次数加1)
    ASLs=(1+7+1+1+2+1+4+2+4)/9=23/9≈2.56
    ASLu:不在散列表中的关键词的平均查找次数(不成功)
    一般方法:将不在散列表中的关键词分若干类。
    如:根据H(key)值分类
    ASLu=(3+2+1+2+1+1+1+9+8+7+6)/11=41/11≈3.73
    参考代码

    #include<iostream>
    #include <stdlib.h>
    using namespace std;
    
    #define SUCCESS 1
    #define UNSUCCESS 0
    #define HASHSIZE 12
    #define NULLKEY -32768
    typedef bool Status ;
    typedef struct 
    {
    	int *elem;
    	int count;
    }HashTable;
    
    int m = 0;   //the length of HashTable
    
    //Initialize  of HashTable
    Status InitHashTable(HashTable* H)
    {
    	int i;
    	m = HASHSIZE;
    	H->count = m;
    	H->elem = (int*)malloc(m*sizeof(int));
    	for(i = 0;i < m; i++)
    	{
    		H->elem[i] = NULLKEY;
    	}
    	return true;
    }
    
    //hash function
    int Hash(int key)
    {
    	return key % m; 
    }
    
    //Insert the elem to the Hahtable
    void InsertHash(HashTable* H,int key)
    {
    	int addr = Hash(key);  //get the address of hash
    	while(H->elem[addr] != NULLKEY)     //Conflict
    		addr = (addr+1) % m;
    	H->elem[addr] = key;
    }
    
    //search the key
    Status searchHash(HashTable H,int key,int *addr)
    {
    	*addr = Hash(key);
    	while(H.elem[*addr] != key)
    	{
    		*addr = (*addr+1) % m;
    		if(H.elem[*addr] == NULLKEY || *addr == Hash(key))
    			//return the origin
    			return UNSUCCESS;
    	}
    	return SUCCESS;
    }
    
    int main()
    {
    	return 0;
    }

    3.1.2平方探测法

    平方探测法以增量序列12,-12,22,-22,……,q2,且q<=⌊TableSize/2⌋循环试探下一个存储地址。还是使用[例1],得到的冲突如下表
    在这里插入图片描述

    ASLs=(1+1+2+1+1+3+1+4+4)/9=18/9=2
    在这里插入图片描述

    参考代码:

    /*******************************************************************************
    功    能:哈希表——平方探测法(+/- 1^2,2^2,3^2...)
    创建时间: 2018-07-24
    作    者:Elvan
    修改时间:
    作    者:
    ********************************************************************************/
    #include <iostream>
    #include <string>
    #include <vector>
    #include <cmath>
    #include <malloc.h>
    using namespace std;
    
    #define MAXTABLESIZE 10000 //允许开辟的最大散列表长度
    typedef int ElementType;
    typedef enum
    {
        Legitimate,
        Empty,
        Deleted
    } EntryType; //散列单元的状态类型
    
    struct HashEntry
    {
        ElementType data; //存放的元素
        EntryType state;  //单元状态
    };
    
    typedef struct HashEntry Cell;
    
    struct TblNode
    {
        int tablesize; //表的最大长度
        Cell *cells;   //存放散列单元数据的数组
    };
    typedef struct TblNode *HashTable;
    
    /*返回大于n且不超过MAXTABLESIZE的最小素数*/
    int NextPrime(int n)
    {
        int p = (n % 2) ? n + 2 : n + 1; //从大于n的下一个奇数开始
        int i;
        while (p <= MAXTABLESIZE)
        {
            for (i = (int)sqrt(p); i > 2; i--)
            {
                if ((p % i) == 0)
                    break;
            }
            if (i == 2)
                break; //说明是素数,结束
            else
                p += 2;
        }
        return p;
    }
    
    /*创建新的哈希表*/
    HashTable CreateTable(int table_size)
    {
        HashTable h = (HashTable)malloc(sizeof(TblNode));
        h->tablesize = NextPrime(table_size);
        h->cells = (Cell *)malloc(h->tablesize * sizeof(Cell));
        //初始化哈希表状态为空单元
        for (int i = 0; i < h->tablesize; i++)
        {
            h->cells[i].state = Empty;
        }
        return h;
    }
    
    /*查找数据的初始位置*/
    int Hash(ElementType key, int n)
    {
        return key % 11; //假设为11
    }
    
    /*查找元素位置*/
    int Find(HashTable h, ElementType key)
    {
        int current_pos, new_pos;
        int collision_num = 0; //记录冲突次数
    
        new_pos = current_pos = Hash(key, h->tablesize); //初始散列位置
    
        //当该位置元素为非空并且不是要找的元素的时候,发生冲突
        while (h->cells[new_pos].state != Empty && h->cells[new_pos].data != key)
        {
            collision_num++;
            if (collision_num % 2) //处理奇数冲突
            {
                new_pos = current_pos + (collision_num + 1) * (collision_num + 1) / 4;
                if (new_pos >= h->tablesize)
                    new_pos = new_pos % h->tablesize;
            }
            else //处理偶数冲突
            {
                new_pos = current_pos - (collision_num) * (collision_num) / 4;
                while (new_pos < 0)
                    new_pos += h->tablesize; //调整到合适大小
            }
        }
        return new_pos;
    }
    
    /*插入新的元素*/
    bool Insert(HashTable h,ElementType key)
    {
        int pos = Find(h,key);  //先查找key是否存在
        if(h->cells[pos].state != Legitimate)
        {
            h->cells[pos].state = Legitimate;
            h->cells[pos].data = key;
            return true;
        }
        else
        {
            cout << "键值已存在!"<<endl;
            return false;
        }
    }
    
    int main(int argc, char const *argv[])
    {
        int a[] = {47,7,29,11,9,84,54,20,30};
        int n = 9;
        HashTable h = CreateTable(n);
        for(int i = 0;i < n;i++)
        {
            Insert(h,a[i]);  //插入元素
        }
        for(int i = 0;i < h->tablesize;i++)
        {
            cout << h->cells[i].data << " ";  //打印哈希表元素
        }
        cout << endl;
        return 0;
    }

    3.2 链地址法

    链地址法就是将相应位置上冲突的所有关键词存储在同一个单链表中
    【例2】 设关键字序列为 47,7,29,11,16,92,22,8,3,50,37,89,94,21,散列函数取为h(key)=keymod11,用分离链接法处理冲突。
    在这里插入图片描述

    【解】表中有9个结点只需1次查找,5个结点需要2次查找,所以查找成功的平均查找次数为
    ASLs=(9+5∗2)/14≈1.36
    参考代码

    /*******************************************************************************
    功    能:哈希表——链表法(+/- 1^2,2^2,3^2...)
    创建时间: 2018-07-24
    作    者:Elvan
    修改时间:
    作    者:
    ********************************************************************************/
    #include <iostream>
    #include <string>
    #include <vector>
    #include <cmath>
    #include <malloc.h>
    using namespace std;
    
    #define MAXTABLESIZE 10000 //允许开辟的最大散列表长度
    #define KEYLENGTH 100      //关键字的最大长度
    
    typedef int ElementType;
    struct LNode
    {
        ElementType data;
        LNode *next;
    };
    typedef LNode *PtrToNode;
    typedef PtrToNode LinkList;
    struct TblNode
    {
        int tablesize;  //表的最大长度
        LinkList heads; //存放散列单元数据的数组
    };
    typedef struct TblNode *HashTable;
    
    /*返回大于n且不超过MAXTABLESIZE的最小素数*/
    int NextPrime(int n)
    {
        int p = (n % 2) ? n + 2 : n + 1; //从大于n的下一个奇数开始
        int i;
        while (p <= MAXTABLESIZE)
        {
            for (i = (int)sqrt(p); i > 2; i--)
            {
                if ((p % i) == 0)
                    break;
            }
            if (i == 2)
                break; //说明是素数,结束
            else
                p += 2;
        }
        return p;
    }
    
    /*创建新的哈希表*/
    HashTable CreateTable(int table_size)
    {
        HashTable h = (HashTable)malloc(sizeof(TblNode));
        h->tablesize = NextPrime(table_size);
        h->heads = (LinkList)malloc(h->tablesize * sizeof(LNode));
        //初始化表头结点
        for (int i = 0; i < h->tablesize; i++)
        {
            h->heads[i].next = NULL;
        }
        return h;
    }
    
    /*查找数据的初始位置*/
    int Hash(ElementType key, int n)
    {
        //这里只针对大小写
        return key % 11;
    }
    
    /*查找元素位置*/
    LinkList Find(HashTable h, ElementType key)
    {
        int pos;
    
        pos = Hash(key, h->tablesize); //初始散列位置
    
        LinkList p = h->heads[pos].next; //从链表的第一个节点开始
        while (p && key != p->data)
        {
            p = p->next;
        }
    
        return p;
    }
    
    /*插入新的元素*/
    bool Insert(HashTable h, ElementType key)
    {
        LinkList p = Find(h, key); //先查找key是否存在
        if (!p)
        {
            //关键词未找到,可以插入
            LinkList new_cell = (LinkList)malloc(sizeof(LNode));
            new_cell->data = key;
            int pos = Hash(key, h->tablesize);
            new_cell->next = h->heads[pos].next;
            h->heads[pos].next = new_cell;
            return true;
        }
        else
        {
            cout << "键值已存在!" << endl;
            return false;
        }
    }
    
    /*销毁链表*/
    void DestroyTable(HashTable h)
    {
        int i;
        LinkList p, tmp;
        //释放每个节点
        for (i = 0; i < h->tablesize; i++)
        {
            p = h->heads[i].next;
            while (p)
            {
                tmp = p->next;
                free(p);
                p = tmp;
            }
        }
        free(h->heads);
        free(h);
    }
    
    int main(int argc, char const *argv[])
    {
        int a[] = {47, 7, 29,29, 11, 16, 92, 22, 8, 3, 50, 37, 89, 94, 21};
        int n = 15;
        HashTable h = CreateTable(n);
        for (int i = 0; i < n; i++)
        {
            Insert(h, a[i]); //插入元素
        }
        for (int i = 0; i < h->tablesize; i++)
        {
            LinkList p = h->heads[i].next;
            while (p)
            {
                cout << p->data << " "; //打印哈希表元素
                p = p->next;
            }
            cout << endl;
        }
        return 0;
    }
    展开全文
  • C++哈希表

    2020-08-24 10:32:03
    哈希表原理3.c++哈希表接口的使用 1.什么是哈希表 散列表(Hash table 也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过关键码值映射到表中一个位置来访问记录,以加快查找的...
  • C++ 哈希表的原理

    2021-01-15 15:01:56
    什么是Hash? Hash也称为散列、哈希。 其基本原理就是把任意长度输入、通过Hash算法变成固定长度...上面就是使用MD5对数据加密一个案例: 原始值:这是一个测试文案 映射后Hash值:2124968af757ed51e71e6abea
  • C++哈希表使用的好文章-Hash_Map

    热门讨论 2010-05-17 12:38:24
    哈希表最大优点,就是把数据存储和查找消耗时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多内存。然而在当前可利用内存越来越多情况下,用空间换时间做法是值得。另外,编码比较容易...
  • 提示:本文是关于C++中哈希表(unordered_map)的使用,看完之后相信你会对C++哈希表的使用有一定的理解 文章目录一、插入和便利二、查找三、修改四、擦除五、交换六、清空七、insert() 的返回值总结 一、插入和...
  • C++ 实现哈希表的实例

    2020-08-29 13:58:28
    主要介绍了C++ 实现哈希表的实例的相关资料,这里使用C++实现哈希表的实例帮助大家彻底理解哈希表的原理,需要的朋友可以参考下
  • 哈希表c++中map与unordered_map区别内部实现机理优缺点以及适用处map 优点map缺点unordered_map 优点unordered_map 缺点unordered_map是hash_map替代名称hash_map原理unordered_map详解1. 原型2. 说明3. 容器...
  • C++标准库中使用的unordered_map底层实现是哈希表,关于哈希表的一些基础知识,我看了公众号代码随想录里面的推文:《关于哈希表,你该了解这些!》,有了基本的认识。 哈希表是什么:哈希表是根据关键码的值而直接...
  • 这意味着我们需要时刻留意哈希表的尺寸以及当前表中已有的元素数量。因为一旦哈希表中有太多元素,也将很难找到可用的位置来存放我们新插入的元素,因此这里我们需要引入一个重要的术语,负载系数(Load Factor)负载...
  • C++哈希表使用教程(STL)

    万次阅读 2013-07-29 11:29:41
    map提供一个很常用功能,那就是提供key-value存储和查找功能。例如,我要记录一个人名和相应存储,而且随时增加,要快速查找和修改: 岳不群-华山派掌门人,人称君子剑 张三丰-武当掌门人,太极拳创始人 ...
  • C++哈希表unordered_map,推荐使用

    千次阅读 2017-03-17 21:37:19
    这题我竟然在想其他歪点子,没想到第二次直接从数组0位置开始遍历(好蠢)(而不是哈希容器),不要动不动就想遍历哈希容器。 class Solution { public: int FirstNotRepeatingChar(string str) { if(str.empty...
  • 这是数据结构中关于哈希表这个知识实现,是使用C++实现,那么希望能够对学校数据结构的哈希表这个知识点朋友能有帮助
  • //建立基本数据类型的哈希表 unordered_map<int,int> m; //<string,string>,<char,char> //向哈希表中添加元素 1.insert 函数 m.insert(pair<...m.begin() //指向哈希表的
  • C++哈希表

    2018-10-04 15:08:58
    C/C++哈希表、字典表 将字符串key,转成整数,使用整数找到对应value;** Hash算法将字符串转成整数,同样Hash值得 key:value会放到一个集合里面,由于Hash能使得不同字符串尽量有不同整数值(仍然有重复); ...
  • 哈希表的原理和使用(C++代码)

    万次阅读 多人点赞 2018-08-12 22:48:32
    概念 散列技术是在记录存储位置和他关键字之间建立一个确定对应关系f,是每个关键字key对应一个存储位置f(key)...采用散列技术将记录存储在一块连续存储空间中,这块连续空间称为散列表或哈希表(Hash-Tab...
  • 采用了普遍使用的三元组思路,由于看网上实现方法感觉略复杂,便想自己用易懂方式自己写一遍。 构建数据结构为,结构体sparse_node,表示矩阵中不为0点。结构体sparse_mat,内部放置sparse_node以及行数和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 635
精华内容 254
关键字:

c++哈希表的使用

c++ 订阅