可并堆,离散优化以及HASH表,线段树总结

来源:互联网 发布:mac装不了软件说版本低 编辑:程序博客网 时间:2024/06/01 11:39

最近学了点数据结构,觉得有必要总结一下。
下面首先是离散优化:

离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
例如:
原数据:1,999,100000,15;处理后:1,3,4,2;
原数据:{100,200},{20,50000},{1,400};
处理后:{3,4},{2,6},{1,5};

离散优化是建立数据与存储结构间的对应关系。
HASH优化:是对于字符串和数字的一种优化方式。它通过将数据映射到数组内的某个元素从而达到节省空间的效果。
但对于f( )可能有f(keyA)==f(keyB) (keyA!=keyB)
这里我们通常有两种解决方法:
1.拉链法
样例

3
abcdefg
gabcedf
kajshdb

给出hash值计算函数和结构体

const int maxn=100000;struct node{    char s[100];    int next;}arc[maxn*10];  int Hash(char str[]){    int i,len,sum=0;    len=strlen(str+1);    for(i=1;i<=len;i++){        sum+=str[i]-'a'+1;    }    return sum%51;}  

易得第一,第二字符串返回的Hash值都是28,发生冲突,可实际上在我看来,无论是否冲突,我们都可以使用静态链接表储存,例如str[]使用fir[Hash(str[])]储存。
如下代码:

int fir[maxn],cur=0;void add(int val,char s[]){    arc[++cur].next=fir[val];    int len=strlen(s+1);    for(int i=1;i<=len;i++)     arc[cur].str[i]=s[i];    fir[val]=cur;//就是普通图论的存法}

2.开地址法

当hash所对密码冲突时,将数据存入另外的位置(可以是下一个空位置,也可以是计算出的任意位置)。

比如:

while(hashtable[ad]!=0){    ad+=ad%3+1;    }

下面来一道例题,[ http://noi.openjudge.cn/ch0305/1551/ ]
题意:
给出一个整数集合s,找到集合中最大的d,让等式a+b+c=d成立,

其中,a,b,c,d是集合S中不同的元素

样例输入:
5

2

3

5

7

12

5

2

16

64

256

1024

0

样例输出:

12

no solution

思路:

a+b+c=d变为a+b=d-c。那么我们就可以通过枚举a+b与d-c的组合进行判断,这里对a+b构建哈希表,table[]数组判断地址使用。对具体值使用data[]数组保存。后来判断减法的时候进入hash函数分别对data[]以及table判断。成立时结束寻找。(即用加法找减法)

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int ADD=536870911; //大质数 int flag[1000020],in[1020],ha1[1000020],ha2[1000020],data[1000000];int hashh(int key){    int ad=((key%1000000)+10061894)%1000000; //ad=(kry%p+P(大质数))%p    while(flag[ad]!=0){        ad+=ad%11+1;        if(ad>1000000) ad%=1000000;//注意不能超过数组     }    return ad;}int find(int key){    int ad=((key%1000000)+10061894)%1000000;    while(data[ad]!=key&&flag[ad]!=0){        ad+=ad%11+1;        if(ad>1000000) ad%=1000000;    }    return flag[ad]==0?-1:ad;//找到空位置跳出(flag[ad]==0)或者因为 找到对应data[ad]==key(即找到对应的a+b时跳出) }int main(){    int n,maxx;    while(scanf("%d",&n)!=EOF&&n!=0){        memset(flag,0,sizeof(flag));        maxx=-1;        for(int i=1;i<=n;i++)            scanf("%d",&in[i]);        for(int i=1;i<=n;i++)            for(int j=i+1;j<=n;j++){                int ans=hashh(in[i]+in[j]);                flag[ans]=1,ha1[ans]=in[i],ha2[ans]=in[j],data[ans]=in[i]+in[j];//保存a,b,a+b;             }        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++){                int ans=find(in[i]-in[j]);                if(i==j||ans==-1) continue;                if(ha1[ans]!=in[i]&&ha1[ans]!=in[j]&&ha2[ans]!=in[i]&&ha2[ans]!=in[j])//因为要求a,b,c,d 是不同元素                     maxx=max(maxx,in[i]); //最大的d值             }        if(maxx!=-1) printf("%d\n",maxx);        else printf("no solution\n");    }    return 0;}

特殊的字符串hahs处理方法—–BKDRHASH:

公式:

Hashvalue[i]=(hashvalue[i-1]*p+str[i])%P
p是一个奇数,且必须大于26;
P是个较大的数
p与P必须互质

下面给一组常用的P :

1e9+7
1e9+9

为了更完美的解决冲突概率问题,下面使用更高级的方法:

双hash

构造两个字符串哈希函数:hash1(),hash2() 如果:hash1(str1)==hash1(str2) &&
hash2(str1)==hash2(str2) 可以断定:str1==str2;

一般双hash函数构造:
hash1[i]=(hash1[i-1]*p+idx(s[i]))%mod1
hash2[i]=(hash2[i-1]*p+idx(s[i]))%mod2
mod1一般取1e9+7,mod2一般取1e9+9
1000000007和1000000009是一对孪生素数,取它们,冲突的概率极低!

下面是BKDRHASH的写法:

未完待续

原创粉丝点击