Hash表的C++实现(转)

原文:Hash表(C++实现)

哈希表的几个概念:

映像:由哈希函数得到的哈希表是一个映像。

冲突:如果两个关键字的哈希函数值相等,这种现象称为冲突。

处理冲突的几个方法:

1、开放地址法:用开放地址处理冲突就是当冲突发生时,形成一个地址序列,沿着这个序列逐个深测,直到找到一个“空”的开放地址,将发生冲突的关键字值存放到该地址中去。

例如:hash(i)=(hash(key)+d(i)) MOD m (i=1,2,3,......,k(k<m-1)) d为增量函数,d(i)=d1,d2,d3,...,dn-1

根据增量序列的取法不同,可以得到不同的开放地址处理冲突探测方法。

有线性探测法、二次方探测法、伪随机探测法。

2、链地址法:把所有关键字为同义词的记录存储在一个线性链表中,这个链表成为同义词链表,即把具有相同哈希地址的关键字值存放在同义链表中。

3、再哈希表:费时间的一种方法

下面是代码:

文件"myhash.h"

#include<iostream>
using namespace std;  

typedef int KeyType; //设关键字域为整形,需要修改类型时,只需修改这里就可以
const int NULLKEY=0; //NULLKEY表示该位置无值
int c=0; //用来统计冲突次数  

struct Elemtype //数据元素类型
{
    KeyType key;
    int ord;
};  

int hashsize[]={11,19,29,37,47}; //hash表容量递增表
int Hash_length=0;//hash表表长  

class HashTable
{
private:
    Elemtype *elem; //数据元素数组,动态申请
    int count;// 当前数据元素个数
    int size; //决定hash表的容量为第几个,hashsize[size]为当前hash容量
public:  

    int Init_HashTable() //构造一个空hash表
    {
        int i;
        count=0;
        size=0; //初始化容量为hashsize[0]=11
        Hash_length=hashsize[0];
        elem=new Elemtype[Hash_length];
        if(!elem)
        {
            cout<<"内存申请失败"<<endl;
            exit(0);
        }
        for(i=0;i<Hash_length;i++)
            elem[i].key=NULLKEY;
        return 1;
    }  

    void Destroy_HashTable()
    {
        delete[]elem;
        elem=NULL;
        count=0;
        size=0;
    }  

    unsigned Hash(KeyType k) //hash函数的一种(取模法)
    {
        return k%Hash_length;
    }  

    void Collision(int &p,int d) //解决冲突
    {
        p=(p+d)%Hash_length; //采用开放地址法里的线性探测
    }  

    bool Search_Hash(KeyType k,int &p) //查找
    {
        //在开放地址hash表中查找关键字等于k的元素
        //若找到用p表示待查数据,查找不成功时,p指向的是可插入地址
        c=0;
        p=Hash(k); //求hash地址
        while(elem[p].key!=NULLKEY && elem[p].key!=k)
        {
            c++;
            if(c<Hash_length)
                Collision(p,c);
            else
                return 0; //表示查找不成功
        }
        if(elem[p].key==k)
            return 1;
        else
            return 0;  

    }  

    int Insert_Hash(Elemtype e) //插入
    {
        //在查找不成功的情况下将k插入到hash表中
        int p;
        if(Search_Hash(e.key,p))
            return -1; //表示该元素已在hash表中
        else if(c<hashsize[size]/2) //冲突次数未达到上限
        {
            //插入e
            elem[p]=e;
            count++;
            return 1;
        }
        else
            ReCreate_HashTable(); // 重建hash表
        return 0; //插入失败
    }  

    void ReCreate_HashTable() //重建hash表
    {
        int i,count2=count;
        Elemtype *p,*elem2=new Elemtype[count];
        p=elem2;
        cout<<"____重建hash表_____"<<endl;
        for(i=0;i<Hash_length;i++) //将原有元素暂存到elem2中
            if(elem[i].key!=NULLKEY)
                *p++=*(elem+i);
        count=0;delete []elem;
        size++; //hash容量增大
        Hash_length=hashsize[size];
        p=new Elemtype[Hash_length];
        if(!p)
        {
            cout<<"空间申请失败"<<endl;
            exit(0);
        }
        elem=p;
        for(i=0;i<Hash_length;i++)
            elem[i].key=NULLKEY;
        for(p=elem2;p<elem2+count2;p++) //将原有元素放回新表
            Insert_Hash(*p);
    }  

    void Traverse_HashTable()
    {
        cout<<"哈希地址0->"<<Hash_length-1<<endl;
        for(int i=0;i<Hash_length;i++)
            if(elem[i].key!=NULLKEY)
                cout<<"元素的关键字值和它的标志分别是:"<<elem[i].key<<"  "<<elem[i].ord<<endl;  

    }  

    void Get_Data(int p)
    {
        cout<<"元素的关键字值和它的标志分别是:"<<elem[p].key<<"  "<<elem[p].ord<<endl;
    }  

};  

测试函数"main.cpp"

#include"myhash.h"  

int main()
{
    Elemtype r[12]={{17,1},{60,2},{29,3},{38,4},{1,5},{2,6},{3,7},{4,8},{5,9},{6,10},{7,11},{8,12}};
    HashTable H;
    int i,p,j;
    KeyType k;
    H.Init_HashTable();
    for(i=0;i<11;i++) //插入前11个记录
    {
        j=H.Insert_Hash(r[i]);
        if(j==-1)
            cout<<"表中已有关键字为"<<r[i].key<<"  "<<r[i].ord<<"的记录"<<endl;
    }  

    cout<<"按哈希地址顺序遍历哈希表"<<endl;
    H.Traverse_HashTable();
    cout<<endl;  

    cout<<"输入要查找的记录的关键字:";
    cin>>k;
    j=H.Search_Hash(k,p);
    if(j==1)
        H.Get_Data(p);
    else
        cout<<"无此记录"<<endl;  

    j=H.Insert_Hash(r[11]); //插入最后一个元素
    if(j==0)
    {
        cout<<"插入失败"<<endl;
        cout<<"需要重建哈希表才可以插入"<<endl;
        cout<<"____重建哈希表____"<<endl;
        H.Insert_Hash(r[i]); //重建后重新插入
    }  

    cout<<"遍历重建后的哈希表"<<endl;
    H.Traverse_HashTable();
    cout<<endl;  

    cout<<"输入要查找的记录的关键字:";
    cin>>k;
    j=H.Search_Hash(k,p);
    if(j==1)
        H.Get_Data(p);
    else
        cout<<"该记录不存在"<<endl;  

    cout<<"____销毁哈希表____"<<endl;
    H.Destroy_HashTable();  

    return 0;
}  

测试结果:

按哈希地址顺序遍历哈希表
哈希地址0->10
元素的关键字值和它的标志分别是:5  9
元素的关键字值和它的标志分别是:1  5
元素的关键字值和它的标志分别是:2  6
元素的关键字值和它的标志分别是:3  7
元素的关键字值和它的标志分别是:4  8
元素的关键字值和它的标志分别是:60  2
元素的关键字值和它的标志分别是:17  1
元素的关键字值和它的标志分别是:29  3
元素的关键字值和它的标志分别是:38  4
元素的关键字值和它的标志分别是:6  10
元素的关键字值和它的标志分别是:7  11  

输入要查找的记录的关键字:5
元素的关键字值和它的标志分别是:5  9
____重建hash表_____
插入失败
需要重建哈希表才可以插入
____重建哈希表____
遍历重建后的哈希表
哈希地址0->18
元素的关键字值和它的标志分别是:38  4
元素的关键字值和它的标志分别是:1  5
元素的关键字值和它的标志分别是:2  6
元素的关键字值和它的标志分别是:3  7
元素的关键字值和它的标志分别是:4  8
元素的关键字值和它的标志分别是:5  9
元素的关键字值和它的标志分别是:60  2
元素的关键字值和它的标志分别是:6  10
元素的关键字值和它的标志分别是:7  11
元素的关键字值和它的标志分别是:8  12
元素的关键字值和它的标志分别是:29  3
元素的关键字值和它的标志分别是:17  1  

输入要查找的记录的关键字:7
元素的关键字值和它的标志分别是:7  11
____销毁哈希表____
Press any key to continue  
时间: 2024-11-15 09:53:37

Hash表的C++实现(转)的相关文章

Hash表的表大小

hash表的出现主要是为了对内存中数据的快速.随机的访问.它主要有三个关键点:Hash表的大小.Hash函数.冲突的解决. 这里首先谈谈第一点:Hash表的大小. Hash表的大小一般是定长的,如果太大,则浪费空间,如果太小,冲突发生的概率变大,体现不出效率.所以,选择合适的Hash表的大小是Hash表性能的关键. 对于Hash表大小的选择通常会考虑两点: 第一,确保Hash表的大小是一个素数.常识告诉我们,当除以一个素数时,会产生最分散的余数,可能最糟糕的除法是除以2的倍数,因为这只会屏蔽被除

南阳OJ-138 找球号(二)(hash表应用)

找球号(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5 描述 在某一国度里流行着一种游戏.游戏规则为:现有一堆球中,每个球上都有一个整数编号i(0<=i<=100000000),编号可重复,还有一个空箱子,现在有两种动作:一种是"ADD",表示向空箱子里放m(0<m<=100)个球,另一种是"QUERY",表示说出M(0<M<=100)个随机整数ki(0<=ki<=100000100),分

python 字典有序无序及查找效率,hash表

刚学python的时候认为字典是无序,通过多次插入,如di = {}, 多次di['testkey']='testvalue' 这样测试来证明无序的.后来接触到了字典查找效率这个东西,查了一下,原来字典在python内部是通过哈希表的顺序来排的,做了一些测试,比如di = {1:1,3:3,2:2,4:4,5:5} ,无论怎么改变键值对的顺序,print di 总是会{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}.所以看起来当插入di['key']='value'时,这组键值对有时

hash表总结

Hash表也称散列表,也有直接译作哈希表,Hash表是一种特殊的数据结构,它同数组.链表以及二叉排序树等相比较有很明显的区别,它能够快速定位到想要查找的记录,而不是与表中存在的记录的关键字进行比较来进行查找.这个源于Hash表设计的特殊性,它采用了函数映射的思想将记录的存储位置与记录的关键字关联起来,从而能够很快速地进行查找. 1.Hash表的设计思想 对于一般的线性表,比如链表,如果要存储联系人信息: 张三 13980593357 李四 15828662334 王五 13409821234 张

hdu 5183 hash表

BC # 32 1002 题意:给出一个数组 a 和一个数 K ,问是否存在数对( i , j ),使 a i   - a i + 1 +……+ (-1)j - i  a j : 对于这道题,一开始就想到了是前缀和,但是如果只是记录下前缀和数组,那么查找就会成为一个大问题.补题的时候一开始考虑用 hash 数组或者是 set 存,但是很明显 TLE 了,在翔神的推荐下,我研究了一下 hash表的创建过程,惊奇地发现,其实就是建了一个 HashMap 结构体,而里面放了这个表所用的数组以及相应操作

nyist oj 138 找球号(二)(hash 表+位运算)

找球号(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5 描述 在某一国度里流行着一种游戏.游戏规则为:现有一堆球中,每个球上都有一个整数编号i(0<=i<=100000000),编号可重复,还有一个空箱子,现在有两种动作:一种是"ADD",表示向空箱子里放m(0<m<=100)个球,另一种是"QUERY",表示说出M(0<M<=100)个随机整数ki(0<=ki<=100000100),分

数据结构与算法-hash表

前言 哈希表是一种存放键-值对的数据结构,其中值用来存放我们真正需要的数据,键的主要目的就是为了找到值.哈希表理想情况下,只需要一次hash计算即可找到值数据,但通常情况下我们不需要耗费巨大的额外空间来追求这丝毫的查找速度(要追求低hash冲突率,必然要扩大hash表),我们更希望的是让空间和时间达到某种平衡,这可以通过调节hash函数来解决(装填因子). 装填因子=表中的记录数/哈希表的长度,如果装填因子越小,表明表中还有很多的空单元,则发生冲突的可能性越小:而装填因子越大,则发生冲突的可能性

hash表的建立和查找

(1)冲突处理方法为:顺次循环后移到下一个位置,寻找空位插入.(2)BKDE 字符串哈希unsigned int hash_BKDE(char *str){/* 初始种子seed 可取31 131 1313 13131 131313 etc.. */unsigned int seed = 131;unsigned int hash = 0;while (*str){hash = hash * seed + (*str++);}return (hash & 0x7FFFFFFF);} 选做内容每一

剑指offer35:第一个只出现一次的字符(hash表使用)

题目:在字符串中找出第一个只出现一次的字符. 分析: 思路一:第一次遍历,从第一个开始,当前字符和以后的字符比较,如何没有相同的,则此字符为所求字符. 此方法可以实现,但时间复杂度很低,为o(n*n) 思路二:(1)只遍历一次,把每种字母对应到一个hash表中 (2)第二次遍历的时候,找到hash中为1的来就是要输出的字母 难点:如何建立hash表那? 我们可知,字符是八位的,我们建立一个256的数组,把每个字符转换为十进制的ascl码,每个字母就对应一个数组位置. #include <iost