哈希:
字符串(數字同理):
例如有100000個字符串,現在要插入一些字符串,插入前比較是否已經存在避免含有重復數據
用暴力計較的話會比較慢,在某字符串插入時,最好的情況是在第一個位置就遇見該字符串,但如果在比較了100000后發現沒有某字符串,然后進行插入,那么比較100000次的比較則是浪費時間
用映射的方法可以很快判斷是否存在某字符串:
1、把100000個字符串存放在經哈希函數返回的哈希地址內
2、將待插入的字符串也經哈希函數,查看返回的哈希地址里是否已經有了某字符串或是否和含有的字符串相等(發生沖突時),沒有就插入
每個串都有自己的哈希地址。這取決於需要一個好的哈希函數(BKDRhash),盡量讓每個字符串的哈希地址不發生沖突。但有時總存在兩個串的哈希地址相同,發生沖突,別急,有解決沖突的辦法。
選用的哈希函數:
哈希函數的目的就是為了產生字符串的哈希值,讓不同的字符串盡量產生不同的哈希值的函數就是好的哈希函數,全然不會產生同樣的哈希函數就是完美的。
處理沖突的方法:
處理沖突的方法有多種,開放定址、拉鏈法、公共溢出區等。
裝載因子,即哈希表的飽和程度:
一般來說裝載因子越小越好。裝載因子越小,碰撞也就越小。哈希表的速度就會越快,但是這樣會大大的浪費空間。假如裝載因子為0.1。那么哈希表僅僅有10%的空間被真正利用。其余的90%都浪費了,這就是時間和空間的矛盾點。為了平衡,如今大部分採用的是0.75作為裝載因子,裝載因子達到0.75,那么就動態添加哈希表的大小。
因此,在編寫代碼之前,首先需要根據所要處理的數據,選擇合適的hash函數和沖突處理辦法。開放定址需要空閑存儲單元,所需要的表比實際容量大,而且容易產生二次聚集發生新沖突。鏈地址使用鏈表存儲關鍵字,可以隨時插入新數據,數據量大小不受限制。缺點是要用到指針,給新單元分配地址需要時間,會一定程度上減慢算法速度,但影響不大可以忽略。
BKDRhash函數代碼如下:
1 unsigned int BKDRHash(char *str) 2 { 3 unsigned int seed = 131313;//也可以乘以31、131、1313、13131、131313.. 4 unsigned int hash = 0; 5 while(*str) 6 { 7 hash = hash*seed + (*str++); 8 } 9 return hash%0x7FFFFFFF;//MAX代表hash表長度 10 }
測試代碼:
#include <iostream> #include <string.h> #include <stdio.h> #include <string.h> #define MAX 40000 using namespace std; struct node { char name[30]; }que[MAX]; /* BKDRHash函數的解析鏈接: http://blog.csdn.net/djinglan/article/details/8812934 */ unsigned int BKDRHash(char *str) { unsigned int seed = 131313;//也可以乘以31、131、1313、13131、131313.. unsigned int hash = 0; while(*str) { hash = hash*seed + (*str++); } return hash%32767;//最好對一個大的素數取余 } int main() { int i=0,n,t,j; char a[MAX][30]; char temp[300]; memset(a,0,sizeof(a)); cin >> n; for(i=0;i<n;i++) { cin >> que[i].name; strcpy(a[BKDRHash(que[i].name)],que[i].name); } cout << "請輸入要查找的字符串:"; while(~scanf("%s",temp)) { if(strcmp(a[BKDRHash(temp)],temp)==0) cout << "yes" << endl; else { cout << "no" << endl; strcpy(a[BKDRHash(temp)],temp); strcpy(que[i++].name,temp); } } for(j=0;j<=i;j++) { cout << que[j].name << endl;//含有的所有字符串 } return 0; }
轉載用拉鏈法解決沖突的方法:http://www.cnblogs.com/liuliuliu/p/3966851.html (小弟認為此定為大神級人物,膜拜O(∩_∩)O哈哈~)
