算法笔记——散列定义及整型散列

散列的定义与整数散列

散列是常用的算法思想之一,在很多程序中都能见到。
经典问题:给出N个正整数,再给出M个正整数,问这M个数中的每个数分别在N个数中是否出现过。
最直观的思路:对每个欲查询的正整数x,遍历所有N个数,看是否有一个数与x相等。这种做法的时间复杂度为O(MN),当M和N很大时,显然这种方法是不可取!

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int N,M;
    cin>>N>>M;
    int a[N],b[M];
    for(int i=0;i<N;i++)
        cin>>a[i];
        for(int j=0;j<M;j++)
            cin>>b[j];
        for(int i=0;i<N;i++)
        for(int j=0;j<M;j++)//时间复杂度为O(NM)
        if(b[j]==a[i])
            cout<<"true"<<endl;
        else
            cout<<"false"<<endl;
        return 0;
}

这时候我们不妨用空间来换时间,即设定一个bool型数组hashTable[100010],其中hashTable[x]==true表示正整数x在N个正整数中出现过,而hashTable[x]==false则表示整数x在N个整数没有出现过。这样就可以在一开始读入N个正整数时就能进行预处理,即当读入的数为x,令hashTable[x]=true,对M个书进行查询时,就那个根据hashTable数组判断出每个数是否出现过,这中算法的时件复杂度为O(N+M);

#include<bits/stdc++.h>
using namespace std;
int main()
{
    bool hashTable[100010]={false};//初始化未出现
    int N,M,x;
    cin>>N>>M;
    for(int i=0;i<N;i++) {
        cin>>x;
        hashTable[x]=true;
    }
    for(int j=0;j<M;j++) {
        cin>>x;
    if(hashTable[x]==true)//时间复杂度为O(N+M)
        cout<<"true"<<endl;
    else
        cout<<"false"<<endl;
    }
    return 0;
}

经典问题:M个欲查询的数中每个数在N个数中出现的次数。
此时bool hashTable[100010]改为int hashTable[100010]

#include<bits/stdc++.h>
using namespace std;
#define Max 100010
int  main()
{
    int hashTable[Max]={0};//初始化次数为0
    int N,M,x;
    cin>>N>>M;
    for(int i=0;i<N;i++) {
        cin>>x;
    hashTable[x]++;
    }
    for(int j=0;j<M;j++) {
        cin>>x;
        cout<<hashTable[x]<<endl;
    }
    return 0;
}

这种方法固然好用,但数组下标数过大时,超过10^9时,此时就会出现错误,那这是该如何解决呢?
*
解决方法就是散列(hash),散列(hash)就是将元素通过一个函数转化为整数,使得该整数可以尽量唯一的代表这个元素。
这个转换函数就叫做散列函数H,元素转换为key,则转换后就是H(key)。
当key是整数时常用方法:直接定址法,平方取中法,除留余数法。

上面的程序用到的就是直接定址法:H(key)=key
平方取中法:取key的平方的中间若干位作为hash值
除留取余法:H(key)=key%mod

把一个很大的数转化为了一个不超过mod的数,把这个数作为数组的下标(表长TSize不能小于mod),mod一般取素数,这样H(key)就能尽可能覆盖[0,mod)范围内的每一个数了,因此为了方便起见,TSize是一个素数,而mod直接取成与TSize相等。

这个时候就会出现一种情况,就是key1,key2,它们的数组下标是一样的,key1已经占用那个位置了,key2就不能再用了。这种情况成为冲突

解决方法

  1. 线性探查法(Linear Probing)
  2. 平方探查法(Quadratic probing)
  3. 链地址法(拉链法)
    具体内容请见: 解决哈希冲突的三种方法.

猜你喜欢

转载自blog.csdn.net/pipihan21/article/details/105152634