哈希表的实现

         这是C++算法基础-数据结构专栏的第三十四篇文章,专栏详情请见此处


引入

        在基础算法专栏中,我有一篇文章:离散化的实现,在这篇文章中,我讲解了离散化的实现,而离散化是一种特殊的哈希(特殊之处在于离散化是保序的),哈希也是一种将较大范围数据映射到一个较小范围中的方法。

        下面我们就来讲哈希表的实现。

定义

        哈希表又称散列表,一种以「key-value」形式存储数据的数据结构。所谓以「key-value」形式存储数据,是指任意的键值 key 都唯一对应到内存中的某个位置。只需要输入查找的键值,就可以快速地找到其对应的 value。可以把哈希表理解为一种高级的数组,这种数组的下标可以是很大的整数,浮点数,字符串甚至结构体。

        对于哈希表的冲突情况,通常有两种解决方法:第一是拉链法,也称开散列法(open hashing);第二是开放寻址法,也称闭散列法。

过程

        主体

        对于给定的一个数据,对它的处理很简单:将其模上一个数,再插到相应的位置上。需要注意,这个模数应该是一个较大的质数(原因在数学上可以证明,较复杂不讲解)。

        取模相信大家都知道如何操作,但对于负数的取模需要注意。数学上,一个数取模后一定是一个非负数,但在C++中,负数取模后会得到一个负数。例如,在数学中-5mod3=1,而在C++中-5 mod 3=-2,这时,对于被模数x我们需要(xmodN+N)modNN为模数)。

        这个做法有一个弊端,很明显,会有一些数取模后会有相同的结果,它们会被映射到同一个位置上,这被称为哈希冲突(哈希碰撞)。哈希冲突有两个常用解决方法。

        拉链法

        对于被映射到同一个位置上的数,我们可以在此位置上开一个单链表,如果有数映射到同一个位置上,只用把他们都放到那个位置的链表里就行了。查询的时候需要把对应位置的链表整个扫一遍,对其中的每个数据比较是否与查询值一致。

        对单链表不太熟悉的可以看看我的这篇文章:单链表的实现

        开放寻址法

        这种方法只需开一个一维数组,直接把数据存储在数组中,如果发生冲突则根据某种方式继续进行探查。比较容易的是线性探查法,若当前位置已经存储数据,则往后一个位置存储,若后一个位置也已经存储数据,则往后两个位置存储......查询的时候也是一样。

        需要注意,开放寻址法中一维数组应该适当地将数组开大,在线性探查法中,数组一般开到指定大小的2~3倍。

        在实际的代码实现中,上述两种方法我们任选一种实现即可。

代码

        下面给出哈希表的实现代码:

//	1.拉链法
    int h[N],e[N],ne[N],idx;

    void insert(int x){
        int k=(x%N+N)%N;
        e[idx]=x;
        ne[idx]=h[k];
        h[k]=idx++;
    }

    bool find(int x){
        int k=(x%N+N)%N;
        for(int i=h[k];i!=-1;i=ne[i]){
        	if(e[i]==x)
                return 1;
		}
        return 0;
    }

//	2.开放寻址法
    int h[N];

    int find(int x){
        int t=(x%N+N)%N;
        while(h[t]!=null&&h[t]!=x){
            t++;
            if(t==N)
                t=0;
        }
        return t;
    }
        代码解释

        在拉链法中,第一行是用单链表实现的哈希表;insert()函数的作用是向哈希表中插入一个数;find()函数的作用是在哈希表中查询某个数是否存在。

        在开放寻址法中,第一行是哈希表,find()函数的作用是在哈希表中查询某个数是否存在,具体来说如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置。


上一篇-堆排序的实现    C++算法基础专栏文章    下一篇-字符串哈希的实现


每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容

点个赞,关注一下呗~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值