面試時常常被問到,什麼是Hash?什麼是Map? html
答:hash採用hash表存儲,map通常採用紅黑樹(RB Tree)實現。所以其memory數據結構是不同的,並且他們的時間複雜度也是不一樣的,hash爲o(1),map爲o(logN)。 c++
Hash,也能夠稱爲「散列」,,就是把任意長度的輸入,經過散列算法,變換成固定長度的輸出,該輸出就是散列值。這是一種壓縮映射,也就是,散列值的空間一般遠小於輸入的空間,不一樣的輸入可能會散列成相同的輸出(也就是多對一的關係)。面試
在全部的線性數據結構中,數組的定位速度最快,由於它可經過數組下標直接定位到相應的數組空間,就不須要一個個查找。而哈希表就是利用數組這個可以快速定位數據的結構解決以上的問題的。算法
"數組能夠經過下標直接定位到相應的空間」,對就是這句,哈希表的作法其實很簡單,就是把Key經過一 個固定的算法函數既所謂的哈希函數轉換成一個整型數字,而後就將該數字對數組長度進行取餘,取餘結果就看成數組的下標,將value存儲在以該數字爲下標 的數組空間裏,而當使用哈希表進行查詢的時候,就是再次使用哈希函數將key轉換爲對應的數組下標,並定位到該空間獲取value,如此一來,就能夠充分 利用到數組的定位性能進行數據定位。數組
例如: 若是一個hash函數是這樣的,安全
index = value % 5;數據結構
以下圖中,左邊爲一個長度爲5的指針數據,下標從0到4,每一個數據元素都是一個鏈表的頭指針,這樣經過value%5就造成了一種一對多的關係,縮小了查找的範圍。ide
雖然咱們不但願發生衝突(同一個key有多個value),但實際上發生衝突的可能性還是存在的。當關鍵字值域遠大於哈希表的長度,並且事先並不知道關鍵字的具體取值時。衝突就不免會發生。另外,當關鍵字的實際取值大於哈希表的長度時,並且表中已裝滿了記錄,若是插入一個新記錄,不只發生衝突,並且還會發生溢出。所以,處理衝突和溢出是哈希技術中的兩個重要問題。通常有開放地址法、鏈地址法。函數
看到了一個叫作One-Way Hash的算法(來自暴雪的hash算法)。性能
若是說兩個不一樣的字符串通過一個哈希算法獲得的入口點一致有可能,但用三個不一樣的哈希算法算出的入口點都一致,那幾乎能夠確定是不可能的事了,這個概率是1:18889465931478580854784,大概是10的 22.3次方分之一,對一個遊戲程序來講足夠安全了。第一個hash值做爲用來定位,另外兩個hash值用來檢測。
/*********************************StringHash.h*********************************/ #pragma once #define MAXTABLELEN 1024 // 默認哈希索引表大小 ////////////////////////////////////////////////////////////////////////// // 哈希索引表定義 typedef struct _HASHTABLE { long nHashA; long nHashB; bool bExists; }HASHTABLE, *PHASHTABLE ; class StringHash { public: StringHash(const long nTableLength = MAXTABLELEN); ~StringHash(void); private: unsigned long cryptTable[0x500]; unsigned long m_tablelength; // 哈希索引表長度 HASHTABLE *m_HashIndexTable; private: void InitCryptTable(); // 對哈希索引表預處理 unsigned long HashString(const string& lpszString, unsigned long dwHashType); // 求取哈希值 public: bool Hash(string url); unsigned long Hashed(string url); // 檢測url是否被hash過 }; /*********************************StringHash.cpp*********************************/ #include "StdAfx.h" #include "StringHash.h" StringHash::StringHash(const long nTableLength /*= MAXTABLELEN*/) { InitCryptTable(); m_tablelength = nTableLength; //初始化hash表 m_HashIndexTable = new HASHTABLE[nTableLength]; for ( int i = 0; i < nTableLength; i++ ) { m_HashIndexTable[i].nHashA = -1; m_HashIndexTable[i].nHashB = -1; m_HashIndexTable[i].bExists = false; } } StringHash::~StringHash(void) { //清理內存 if ( NULL != m_HashIndexTable ) { delete []m_HashIndexTable; m_HashIndexTable = NULL; m_tablelength = 0; } } /************************************************************************/ /*函數名:InitCryptTable /*功 能:對哈希索引表預處理 /*返回值:無 /************************************************************************/ void StringHash::InitCryptTable() { unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i; for( index1 = 0; index1 < 0x100; index1++ ) { for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 ) { unsigned long temp1, temp2; seed = (seed * 125 + 3) % 0x2AAAAB; temp1 = (seed & 0xFFFF) << 0x10; seed = (seed * 125 + 3) % 0x2AAAAB; temp2 = (seed & 0xFFFF); cryptTable[index2] = ( temp1 | temp2 ); } } } /************************************************************************/ /*函數名:HashString /*功 能:求取哈希值 /*返回值:返回hash值 /************************************************************************/ unsigned long StringHash::HashString(const string& lpszString, unsigned long dwHashType) { unsigned char *key = (unsigned char *)(const_cast<char*>(lpszString.c_str())); unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE; int ch; while(*key != 0) { ch = toupper(*key++); seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2); seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3; } return seed1; } /************************************************************************/ /*函數名:Hashed /*功 能:檢測一個字符串是否被hash過 /*返回值:若是存在,返回位置;不然,返回-1 /************************************************************************/ unsigned long StringHash::Hashed(string lpszString) { const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2; //不一樣的字符串三次hash還會碰撞的概率無限接近於不可能 unsigned long nHash = HashString(lpszString, HASH_OFFSET); unsigned long nHashA = HashString(lpszString, HASH_A); unsigned long nHashB = HashString(lpszString, HASH_B); unsigned long nHashStart = nHash % m_tablelength, nHashPos = nHashStart; while ( m_HashIndexTable[nHashPos].bExists) { if (m_HashIndexTable[nHashPos].nHashA == nHashA && m_HashIndexTable[nHashPos].nHashB == nHashB) return nHashPos; else nHashPos = (nHashPos + 1) % m_tablelength; if (nHashPos == nHashStart) break; } return -1; //沒有找到 } /************************************************************************/ /*函數名:Hash /*功 能:hash一個字符串 /*返回值:成功,返回true;失敗,返回false /************************************************************************/ bool StringHash::Hash(string lpszString) { const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2; unsigned long nHash = HashString(lpszString, HASH_OFFSET); unsigned long nHashA = HashString(lpszString, HASH_A); unsigned long nHashB = HashString(lpszString, HASH_B); unsigned long nHashStart = nHash % m_tablelength, nHashPos = nHashStart; while ( m_HashIndexTable[nHashPos].bExists) { nHashPos = (nHashPos + 1) % m_tablelength; if (nHashPos == nHashStart) //一個輪迴 { //hash表中沒有空餘的位置了,沒法完成hash return false; } } m_HashIndexTable[nHashPos].bExists = true; m_HashIndexTable[nHashPos].nHashA = nHashA; m_HashIndexTable[nHashPos].nHashB = nHashB; return true; }
快速查找,刪除的基本數據結構,一般須要總數據量能夠放入內存。
Map是c++標準庫STL提供的一類關聯式容器,提供key-value的存儲和查找功能。
Map是基於紅黑樹的(一樣set也是),那麼它的查找速度是log(n)級別的。
它的優勢是佔用內存小。
權衡三個因素: 查找速度, 數據量, 內存使用。
整體來講,hash查找速度會比map快,並且查找速度基本和數據量大小無關,屬於常數級別;而map的查找速度是log(n)級別。並不必定常數就比log(n) 小,hash還有hash函數的耗時,明白了吧,若是你考慮效率,特別是在元素達到必定數量級時,考慮考慮hash。但若你對內存使用特別嚴格, 但願程序儘量少消耗內存,那麼必定要當心,hash可能會讓你陷入尷尬,特別是當你的hash對象特別多時,你就更沒法控制了,並且 hash的構造速度較慢。
http://baike.baidu.com/view/20089
http://blog.csdn.net/v_july_v/article/details/6256463
http://panlianghui-126-com.iteye.com/blog/968057
http://singlelove1983.blog.163.com/blog/static/50849047200863122550370/
http://xdeduzb.blog.163.com/blog/static/8199363720111105949345/
http://www.cnblogs.com/ylan2009/archive/2012/04/13/2445101.html
本做品採用 知識共享署名-非商業性使用-相同方式共享 3.0 未本地化版本許可協議進行許可。歡迎轉載,請註明出處:
轉載自: cococo點點 http://www.cnblogs.com/coder2012