詳細資料:http://www.oschina.net/code/snippet_99767_1217
1.一個Hash演算法首先要有一個Hash表:
//////////////////////////////////////////////////////////////////////////<br />// 雜湊索引表定義<br />typedef struct _HASHTABLE<br />{<br />long nHashA;<br />long nHashB;<br />bool bExists;<br />}HASHTABLE, *PHASHTABLE ;<br />m_tablelength = nTableLength;<br />//初始化hash表<br />m_HashIndexTable = new HASHTABLE[nTableLength];<br />for ( int i = 0; i < nTableLength; i++ )<br />{<br />m_HashIndexTable[i].nHashA = -1;<br />m_HashIndexTable[i].nHashB = -1;<br />m_HashIndexTable[i].bExists = false;<br />}
2.還要有一個壓縮演算法,把字串壓縮成32位不帶正負號的整數:
unsigned long StringHash::HashString(const string& lpszString, unsigned long dwHashType)<br />{<br />unsigned char *key = (unsigned char *)(const_cast<char*>(lpszString.c_str()));<br />unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;<br />int ch;<br />while(*key != 0)<br />{<br />ch = toupper(*key++);<br />seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);<br />seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;<br />}<br />return seed1;<br />}
3.在上面那個壓縮演算法中有一個cryptTable,我們要先把它處理一下:
void StringHash::InitCryptTable()<br />{<br />unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;<br />for( index1 = 0; index1 < 0x100; index1++ )<br />{<br />for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )<br />{<br />unsigned long temp1, temp2;<br />seed = (seed * 125 + 3) % 0x2AAAAB;<br />temp1 = (seed & 0xFFFF) << 0x10;<br />seed = (seed * 125 + 3) % 0x2AAAAB;<br />temp2 = (seed & 0xFFFF);<br />cryptTable[index2] = ( temp1 | temp2 );<br />}<br />}<br />}
4.現在就可以Hash一個字串了,我們調用了三次壓縮演算法,擷取了三個hash值,第一個hash值用來計算其在hash表中的索引,還有兩個用來處理碰撞情況(兩個不同的字串三次hash的結果是一樣的機率基本為0)
bool StringHash::Hash(<br />string lpszString, //url<br />const PLinkNode node //url 對應節點<br />)<br />{<br />const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;<br />unsigned long nHash = HashString(lpszString, HASH_OFFSET);<br />unsigned long nHashA = HashString(lpszString, HASH_A);<br />unsigned long nHashB = HashString(lpszString, HASH_B);<br />unsigned long nHashStart = nHash % m_tablelength,<br />nHashPos = nHashStart;<br />while ( m_HashIndexTable[nHashPos].bExists)<br />{<br />nHashPos = (nHashPos + 1) % m_tablelength;<br />if (nHashPos == nHashStart) //一個輪迴<br />{<br />//hash表中沒有空餘的位置了,無法完成hash<br />return false;<br />}<br />}<br />m_HashIndexTable[nHashPos].bExists = true;<br />m_HashIndexTable[nHashPos].nHashA = nHashA;<br />m_HashIndexTable[nHashPos].nHashB = nHashB;<br />return true;<br />}
5.我們還需要一個介面,用來判斷一個字串是否被hash過
unsigned long StringHash::Hashed(const string& url)<br />{<br />const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;<br />//不同的字串三次hash還會碰撞的幾率無限接近於不可能<br />unsigned long nHash = HashString(url, HASH_OFFSET);<br />unsigned long nHashA = HashString(url, HASH_A);<br />unsigned long nHashB = HashString(url, HASH_B);<br />unsigned long nHashStart = nHash % m_tablelength,<br />nHashPos = nHashStart;<br />while ( m_HashIndexTable[nHashPos].bExists)<br />{<br />if (m_HashIndexTable[nHashPos].nHashA == nHashA && m_HashIndexTable[nHashPos].nHashB == nHashB)<br />return nHashPos;<br />else<br />nHashPos = (nHashPos + 1) % m_tablelength;<br />if (nHashPos == nHashStart)<br />break;<br />}<br />return -1; //沒有找到<br />}
至此,一個簡單的字串hash演算法就完成了。
---------------------------------------------------------------------------------------------------------------------------------------------------
總結:
一開始對下面的代碼不是很理解
while ( m_HashIndexTable[nHashPos].bExists)<br />{<br />nHashPos = (nHashPos + 1) % m_tablelength;<br />if (nHashPos == nHashStart) //一個輪迴<br />{<br />//hash表中沒有空餘的位置了,無法完成hash<br />return false;<br />}<br />}
其實,就是一個hash值與其他的碰撞了,那就先來後到,挑下面相鄰的地方;這並不會影響Hashed()的匹配,匹配時也是按照這樣的方法,並且還要匹配那兩個hash值,有點像從一個房間開始,挨個敲門,看裡面是不是要找的人!
所以說這是一個空間換時間的演算法,hash表越大,發生碰撞的可能性就越小,演算法複雜度就越接近於O(1);