哈希/散列(hash)

哈希:node

大意:通俗点讲 就是通常咱们对有些要查找统计的题,在咱们输入时就把他们分红几类(经过某些关系),等回过头查找时  直接在属于他的分区找就是了 这样就大大缩短了时间。这里的某些关系又被称为哈希函数。固然  哈希不止这些做用  在网络安全方面也是一大功臣。web

官方定义:哈希算法将任意长度的二进制映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值哈希值是一段数据惟一且极其紧凑的数值表示形式。若是散列一段明文并且哪怕只更改该段落的一个字母,随后的哈希都将产生不一样的值。要找到散列为同一个值的两个不一样的输入,在计算上是不可能的,因此数据的哈希值能够检验数据的完整性。通常用于快速查找和加密算法。算法

构造哈希函数一般由如下途径:数组

 1. 直接寻址法取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a•key + b,其中a和b为常数(这种散列函数叫作自身函数)安全

        2. 数字分析法分析一组数据,好比一组员工的出生年月日,这时咱们发现出生年月日的前几位数字大致相同,这样的话,出现冲突的概率就会很大,可是咱们发现年月日的后几位表示月份和具体日期的数字差异很大,若是用后面的数字来构成散列地址,则冲突的概率会明显下降。所以数字分析法就是找出数字的规律,尽量利用这些数据来构造冲突概率较低的散列地址。网络

      3. 平方取中法:取关键字平方后的中间几位做为散列地址。less

   4. 折叠法:将关键字分割成位数相同的几部分,最后一部分位数能够不一样,而后取这几部分的叠加和(去除进位)做为散列地址。ide

   5. 随机数法:选择一随机函数,取关键字的随机值做为散列地址,一般用于关键字长度不一样的场合。函数

   6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p, p<=m。不只能够对关键字直接取模,也可在折叠、平方取中等运算以后取模。对p的选择很重要,通常取素数或m,若p选的很差,容易产生同义词。this

方法1和方法6常会用到

 当咱们要输入不少数据时 可能会出现两个数对应的哈希值同样  这就是哈希冲突,解决冲突的方法也有好几种 

       开放定址法

开放定址法就是从发生冲突的那个单元开始,按照必定的次序,从散列表中查找出一个空闲的存储单元,把发生冲突的待插入元素存入到该单元中的一类处理冲突的方法。在开放定址法中,散列表中的空闲单元(假定下标为d)不只向散列地址为d的同义词元素开放,即容许它们使用,并且向发生冲突的其它元素开放,因它们的散列地址不为d,因此称为非同义词元素。总之,在开放定址法中,空闲单元既向同义词元素开放,也向发生冲突的非同义词元素开放,此方法的名称也由此而来。在使用开放定址法处理冲突的散列表中,下标为d的单元最终存储的是同义词中的一个元素,仍是其它元素,就看谁先占用它。查找一个元素的过程是:首先根据给定的关键字K,利用与插入时使用的同一散列函数h计算出散列地址(假定为下标d),而后,用K同d单元的关键字进行比较,若相等则查找成功,不然按照插入时处理冲突的相同次序,依次用K同所查单元的关键字进行比较,直到查找成功或查找到一个空单元(代表查找失败)为止。
在开放定址法中,从发生冲突的散列地址为d的单元起进行查找有多种方法,每一种都对应着必定的查找次序,所通过的单元构成了一条查找路径或称探查序列。在查找的多种方法中,主要有线性探查法、平方探查法和双散列函数探查法等。
(1)线性探查法
线性探查法是用开放定址法处理冲突的一种最简单的探查方法,它从发生冲突的d单元起,依次探查下一个单元(当达到下标为m—l的表尾单元时,下一个探查的单元是下标为O的表首单元,即把散列表看做为首尾相接的循环表),直到碰到一个空闲单元或探查完全部单元为止。这种方法的探查序列为d,d+l,d+2,…,或表示为(d+i)%m(O≤i≤m—1)。
固然,这里的i在最坏的状况下才能取值到m—1,通常只需取前几个值就可能找到一个空闲单元。找到一个空闲单元后,把发生冲突的待插入元素存入该单元便可。
(2)平方探查法
平方探查法是一种较好的处理冲突的方法,它可以较好地避免堆积现象。它的缺点是不能探查到散列表上的全部单元,但至少能探查到一半单元(证实从略)。例如,当do=5,m=17时,只能探查到下标依次为5,6,9,14,4,13,7,3,1的单元,而不能探查到剩余的单元。不过在实际应用中,能探查到一半单元也就能够了。若探查到一半单元仍找不到一个空闲单元,代表此散列表太满,应该从新创建。
(3)双散列函数探查法
这种方法使用两个散列函数hl和h2。其中hl和前面的h同样,以关键字为自变量,产生一个0至m—l之间的数做为散列地址;h2也以关键字为自变量,产生一个l至m—1之间的、并和m互素的数(即m不能被该数整除)做为探查序列的地址增量(即步长),探查序列的步长值是固定值l;对于平方探查法,探查序列的步长值是探查次数i的两倍减l;对于双散列函数探查法,其探查序列的步长值是同一关键字的另外一散列函数的值。

2    拉链法

(1)拉链法解决冲突的方法
     拉链法解决冲突的作法是:将全部关键字为同义词的结点连接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各份量的初值均应为空指针。在拉链法中,装填因子α能够大于1,但通常均取α≤1。

【例9.2】已知一组关键字和选定的散列函数和例9.1相同,用拉链法解决冲突构造这组关键字的散列表。
  解答:不妨和例9.1相似,取表长为13,故散列函数为h(key)=key%13,散列表为T[0..12]。
注意:
     当把h(key)=i的关键字插入第i个单链表时,既可插入在链表的头上,也能够插在链表的尾上。这是由于必须肯定key不在第i个链表时,才能将它插入表中,因此也就知道链尾结点的地址。若采用将新关键字插入链尾的方式,依次把给定的这组关键字插入表中,则所获得的散列表以下图所示。
      

(2)拉链法的优势
     与开放定址法相比,拉链法有以下几个优势:
  (1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,所以平均查找长度较短;
  (2)因为拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前没法肯定表长的状况;
  (3)开放定址法为减小冲突,要求装填因子α较小,故当结点规模较大时会浪费不少空间。而拉链法中可取α≥1,且结点较大时,拉链法中增长的指针域可忽略不计,所以节省空间;
  (4)在用拉链法构造的散列表中,删除结点的操做易于实现。只要简单地删去链表上相应的结点便可。而对开放地址法构造的散列表,删除结点不能简单地将被删结点的空间置为空,不然将截断在它以后填人散列表的同义词结点的查找路径。这是由于各类开放地址法中,空地址单元(即开放地址)都是查找失败的条件。所以在用开放地址法处理冲突的散列表上执行删除操做,只能在被删结点上作删除标记,而不能真正删除结点。

(3)拉链法的缺点
     拉链法的缺点是:指针须要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可以使装填因子变小,这又减小了开放定址法中的冲突,从而提升平均查找速度。

/*
*哈希表 拉链法
*/
#include<stdio.h>
#include<stdlib.h>
#define MinTableSize 10
typedef int ElemType;
typedef unsigned int Index;
typedef struct ListNode
{
 ElemType element;
 struct ListNode *next;
}*Position;
typedef Position List;
/* List *TheList will be an array of lists, allocated later */
/* The lists use headers (for simplicity), */
/* though this wastes space */
typedef struct HashTbl
{
 int TableSize;
  List *TheLists;
}*HashTable;
int NextPrime(int N)
{
 int i;
 if(N%2==0)
  N++;
 for(;;N+=2)
 {
  for(i=3;i*i<=N;i+=2)
   if(N%i==0)
    return 0;
  return N; 
 }
}
/*Hash function for ints*/
Index Hash(ElemType Key,int TableSize)
{
 return Key%TableSize;
}
HashTable InitializeTable(int TableSize)
{
 HashTable H;
 int i;
 if(TableSize<MinTableSize)
 {
  printf("Table size too small!\n");
  return NULL;
 }
 
 /*Allocate table*/
 H=(HashTable)malloc(sizeof(struct HashTbl));
 if(NULL==H)
   printf("Out of space!!!\n");
 H->TableSize=NextPrime(TableSize);
 
 /*Allocate array of lists*/
 H->TheLists=(List *)malloc(sizeof(List)*H->TableSize);
 if(NULL==H->TheLists)
 {
   printf("Out of space!!!\n");
   free(H);
   return NULL;
 }
 /*Allocate list  headers*/
 for(i=0;i<H->TableSize;i++)
 {
  H->TheLists[i]=(Position)malloc(sizeof(struct ListNode));
  if(NULL==H->TheLists[i])
   printf("Out of space!!!\n");
  else
   H->TheLists[i]->next=NULL;
  H->TheLists[i]->element=0;//哈希表中全部元素的key初始化为0
 }
 return H;
}
Position Find(ElemType Key,HashTable H)
{
 Position p;
 List L;
 L=H->TheLists[Hash(Key,H->TableSize)];
 p=L->next;
 while(p!=NULL&&p->element!=Key)/*Probably need strcmp!!*/
  p=p->next;
 return p;
}
void Insert(ElemType Key,HashTable H)
{
 Position pos,newCell;
 List L;
 pos=Find(Key,H);
 if(NULL==pos)/*Key is not found*/
 {
  newCell=(Position)malloc(sizeof(struct ListNode));
  if(NULL==newCell)
    printf("Out of space!!!"); 
  else
  {
   L=H->TheLists[Hash(Key,H->TableSize)];
   newCell->next=L->next;
   newCell->element=Key;/*Probably need strcpy*/
   L->next=newCell;
  }
 }
}
void DestroyTable(HashTable H)
{
 int i;
 for(i=0;i<H->TableSize;i++)
 {
  Position p=H->TheLists[i];
  Position temp;
  while(p!=NULL)
  {
   temp=p->next;
   free(p);
   p=temp;
  }
 }
 free(H->TheLists);
 free(H);
}
void printHash(HashTable H,int len)
{
 int i;
 for(i=0;i<len;i++)
 {
  Position p=H->TheLists[i];
  while(p)
  {
   printf("address=%d value=%d\n",i,p->element);
   p=p->next;
  } 
 }
}
int main()
{
 
 HashTable H;
 Position p=NULL;
 int array[]={19,14,23,01,68,20,84,27,55,11,10,79};
 int len=sizeof(array)/sizeof(array[0]);
 int i;
 ElemType k;
          
 H=InitializeTable(len);
 for(i=0;i<len;i++)
 {
  Insert(array[i],H); 
 }
 printHash(H,len);
 printf("\n\n");
 
 printf("please input the value which need find:");
 scanf("%d",&k);
 p=Find(k,H);
 if(p)
   printf("%d",p->element);
 else
   printf("cannot find the value!");
 printf("\n\n");
  
 printf("free the table\n");
 DestroyTable(H);
 printf("it's done!!!");
 printf("\n\n");
 return 0;
}

例题:

 

A - SnowflakeSnowSnowflakes

Crawling in process... Crawling failed Time Limit:4000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

Submit Status Practice POJ 3349

Description

You may have heard that no two snowflakes are alike. Your task is to write a program to determine whether this is really true. Your program will read information about a collection of snowflakes, and search for a pair that may be identical. Each snowflake has six arms. For each snowflake, your program will be provided with a measurement of the length of each of the six arms. Any pair of snowflakes which have the same lengths of corresponding arms should be flagged by your program as possibly identical.

Input

The first line of input will contain a single integer n, 0 < n ≤ 100000, the number of snowflakes to follow. This will be followed by n lines, each describing a snowflake. Each snowflake will be described by a line containing six integers (each integer is at least 0 and less than 10000000), the lengths of the arms of the snow ake. The lengths of the arms will be given in order around the snowflake (either clockwise or counterclockwise), but they may begin with any of the six arms. For example, the same snowflake could be described as 1 2 3 4 5 6 or 4 3 2 1 6 5.

Output

If all of the snowflakes are distinct, your program should print the message:
No two snowflakes are alike.
If there is a pair of possibly identical snow akes, your program should print the message:
Twin snowflakes found.

Sample Input

2 1 2 3 4 5 6 4 3 2 1 6 5

Sample Output

Twin snowflakes found.

 

题意:

    在n (n<100000)个雪花中判断是否存在两片彻底相同的雪花,每片雪花有6个角,每一个角的长度限制为1000000

    两片雪花相等的条件:

    雪花6个角的长度按顺序相等(这个顺序便可以是顺时针的也能够是逆时针的)

code:

#include<stdio.h>
#include<string.h>
#define M 200000
#define F 100000
struct NODE
{
    int data,next;
} node[M];
int a[M][6]={0},ha[M]={0};
int judge(int k1,int k2)//开放定址法
{
    int ar[20],i,j,k,ans;
    for(i=0;i<6;i++) 
           ar[i]=ar[i+6]=a[k1][i];
    for(i=0;i<6;i++)
    {
        ans = 1;
        for(j = 0; j < 6; j++)
            if (ar[i+j]!=a[k2][j])
                ans = 0;
        if (ans)
            return 1;
    }
    for(i=0;i<6;i++)
        ar[5-i]=ar[5-i+6]=a[k1][i];
    for(i=0;i<6;i++)
    {
        ans=1;
        for(j=0;j<6;j++)
            if(ar[i+j]!=a[k2][j])
                ans=0;
        if (ans)
            return 1;
    }
    return 0;
}
int hash(int k)//除留余数法
{
    int i,sum=0;
    for(i=0;i<6;i++)
        sum=(sum+a[k][i])%F;
    return sum;
}
int main()
{
    int  i,j,n,temp,t,k,l=1;
    memset(node,0,sizeof(node));
    scanf("%d",&n);
    for(i = 1; i <= n; i++)
    {
        for(j = 0; j <  6; j++)
            scanf("%d",&a[i][j]);
        t = hash(i);
        if (ha[t] == 0)
        {
            ha[t] = l;
            node[l].data = i;
            node[l++].next = 0;
        }
        else
        {
            temp=ha[t];
            while(temp!=0)
            {
                if(judge(node[temp].data,i))
                {
                    printf("Twin snowflakes found.\n");
                    return 0;
                }
                else
                    temp=node[temp].next;
            }
            node[l].next=ha[t];
            node[l].data=i;
            ha[t]=l++;
        }
    }
    printf("No two snowflakes are alike.\n");
    return 0;
}
相关文章
相关标签/搜索