哈希: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常會用到
當咱們要輸入不少數據時 可能會出現兩個數對應的哈希值同樣 這就是哈希衝突,解決衝突的方法也有好幾種
1 開放定址法:
開放定址法就是從發生衝突的那個單元開始,按照必定的次序,從散列表中查找出一個空閒的存儲單元,把發生衝突的待插入元素存入到該單元中的一類處理衝突的方法。在開放定址法中,散列表中的空閒單元(假定下標爲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; }