咱們一直在講哈希,哈希,可是真正用這個數據結構的時候每每採用的是它的簡化形式ios
那麼如何構造一個真正的哈希表呢?數組
首先咱們明確一下哈希表是幹啥用的,沒錯就是用來判重和查找的數據結構
可是這個判重,咱們要規定一下限制範圍, 雖然哈希表功能強大可是仍是有侷限性函數
哈希表適合那種數據特別多,可是對於每個數據,它的值不是很大的狀況spa
在使用哈希表以前,咱們先來進行一個科普,C++的int轉string與string轉intcode
在這裏最好不用調用什麼函數,你不懂它blog
string轉int istringstream is("12"); //構造輸入字符串流,流的內容初始化爲「12」的字符串 int i; is >> i; //從is流中讀入一個int整數存入i中 int轉string ostringstream os; //構造一個輸出字符串流,流內容爲空 int i = 12; os << i; //向輸出字符串流中輸出int整數i的內容 cout << os.str() << endl; //利用字符串流的str函數獲取流中的內容
首先咱們給出哈希表的一些定義內存
const int max_hash_size=1000005; const int maxn=1005; int head[max_hash_size]; //存數據的數組 int Next[maxn]; //記錄同地址元素的下一個元素的值 int st[maxn]; //數據集
爲啥這個max_hash_size很大,由於這個範圍是每個元素最大是多少而不是一共有多少個元素,哈希表就是用來解決元素很是多的那種狀況的字符串
若是單純使用bool數組的話受制於內存的限制,查找能力實在有限,哈希表在本質上就是拓寬了這個bool數組的範圍string
這裏的head和Next就是鏈表,而後咱們給出哈希函數
int Hash(int& s) { return(s*2654435769)>>28; } int HashingDouble(double d) { if (d==0) return 0; else { int exponent; double mantissa = frexp(d, &exponent); return (unsigned int)((2*mantissa-1) * (~0U)); } } int HashingString(char *str, int iLen) { int hsval = 2654435769; int i; int iShift = 0; for(i=0; i<iLen; i++) { hsval ^= (str[i]<<iShift); iShift+=3; if(iShift>24) iShift=0; } return hsval; }
你能夠把哈希函數理解爲根據元素的值來計算這個元素下標的函數,而後咱們回頭去看那個max_hash_size,而後理解一下:
若是咱們不用哈希函數,那麼數據有多少,max_hash_size就是多少,若是咱們用了哈希函數,就把原數據範圍進行了一個放縮,放縮到了空間所容許的一個範圍內
這個時候咱們再取理解maxn是啥,既然max_hash_size是通過放縮以後的數據有多少個,那麼maxn就是每個數據的數據範圍了
因爲哈希碰撞的緣由,同哈希值元素可能有多個,咱們在鏈表查找到地址的時候,要順着鏈表一個一個找看是否是
下面給出嘗試插入哈希表的函數,若是不重複就能夠插進去,不然就插不進去返回0
int try_to_insert(int s) { int h=Hash(st[s]); //獲得s元素的數組下標 int u=head[h]; //獲得這個下標下的元素值 while(u) //順着鏈表一個一個找 { if(st[u]==st[s]) //若是在鏈表中找到了s,說明這個s已經訪問過了 return 0; u=Next[u]; //此時u是下一個在此位置的元素值 } Next[s]=head[h]; //頭插法 head[h]=s; return 1; }
由於實現的過程當中有一個
int u=head[h];
while(u)
這麼一個操做,這應該是這種方法實現哈希表的一個缺陷,因此在插入的時候,千萬保證非0
下面給出哈希表完整的實現代碼,若是有更好的實現方法,會在本篇文章的基礎上迭代
1 //適用於數據特別多可是每一個數的範圍不是很大的狀況下查找 2 /* 3 string轉int 4 istringstream is("12"); //構造輸入字符串流,流的內容初始化爲「12」的字符串 5 int i; 6 is >> i; //從is流中讀入一個int整數存入i中 7 int轉string 8 ostringstream os; //構造一個輸出字符串流,流內容爲空 9 int i = 12; 10 os << i; //向輸出字符串流中輸出int整數i的內容 11 cout << os.str() << endl; //利用字符串流的str函數獲取流中的內容 12 */ 13 #include<iostream> 14 #include<cstring> 15 #include<cmath> 16 using namespace std; 17 const int max_hash_size=1000005; 18 const int maxn=1005; 19 int head[max_hash_size]; //存數據的數組 20 int Next[maxn]; //記錄同地址元素的下一個元素的值 21 int st[maxn]; //數據集 22 void init_lookup_table() 23 { 24 memset(head,0,sizeof(head)); 25 } 26 int Hash(int& s) 27 { 28 return(s*2654435769)>>28; 29 } 30 int HashingDouble(double d) 31 { 32 if (d==0) 33 return 0; 34 else 35 { 36 int exponent; 37 double mantissa = frexp(d, &exponent); 38 return (unsigned int)((2*mantissa-1) * (~0U)); 39 } 40 } 41 int HashingString(char *str, int iLen) 42 { 43 int hsval = 2654435769; 44 int i; 45 int iShift = 0; 46 for(i=0; i<iLen; i++) 47 { 48 hsval ^= (str[i]<<iShift); 49 iShift+=3; 50 if(iShift>24) 51 iShift=0; 52 } 53 return hsval; 54 } 55 int try_to_insert(int s) 56 { 57 int h=Hash(st[s]); //獲得s元素的數組下標 58 int u=head[h]; //獲得這個下標下的元素值 59 while(u) //順着鏈表一個一個找 60 { 61 if(st[u]==st[s]) //若是在鏈表中找到了s,說明這個s已經訪問過了 62 return 0; 63 u=Next[u]; //此時u是下一個在此位置的元素值 64 } 65 Next[s]=head[h]; //頭插法 66 head[h]=s; 67 return 1; 68 } 69 int main() 70 { 71 //必須保證數據集中,查找的位置有值 72 for(int i=1;i<=10;i++) 73 st[i]=i-1; 74 init_lookup_table(); 75 for(int i=1;i<=5;i++) 76 { 77 if(try_to_insert(i)) 78 cout<<"Yes"<<endl; 79 else 80 cout<<"No"<<endl; 81 } 82 cout<<endl; 83 for(int i=3;i<=7;i++) 84 { 85 if(try_to_insert(i)) 86 cout<<"Yes"<<endl; 87 else 88 cout<<"No"<<endl; 89 } 90 return 0; 91 }