哈希表(Hash table,也叫散列表),是根據鍵(Key)而直接訪問在內存存儲位置的數據結構。 也就是說,它經過計算一個關於鍵值的函數,將所需查詢的數據映射到表中一個位置來訪問記錄,這加快了查找速度。 這個映射函數稱作散列函數,存放記錄的數組稱作散列表。 ————維基百科php
在一些合理的假設下,在哈希表中的全部操做的時間複雜度能夠簡單看做O(1)。它經過計算一個鍵的哈希值來快速定位鍵值在哈希表的位置。實現一個好的哈希表的關鍵是一個好的哈希算法與一個處理哈希衝突的方法
。常見的簡單哈希算法有BKDRHash,APHash,DJBHash,JSHash,RSHash.較複雜有MD5和SHA1之流。哈希算法的好壞直接影響哈希表存取的效率。而處理哈希衝突的辦法有開放尋址法與拉鍊法。下面示例使用的是拉鍊法html
上述多個哈希算法中 BKDRHash在效率,平衡,均勻分佈方面表現突出,而且它的實現也是最容易理解與記憶的。實現以下:git
/** * BKDR hash function in clang * @param key char* 字符串 * @return int hashed value, we will get a int value */ int bkdrHash( char *key ) { uint seed = 131;//i can be 13, 131, 1313,131313 etc uint hash = 0; while( *key != '\n' && *key != 0 ) { hash = hash * seed + ( *key++ ); } return ( hash & 0x7FFFFFFF ); }
示意圖:(圖片來源:http://blog.csdn.net/v_JULY_v...)
github
上圖中,左則是一個數組,保存的是每一個單向鏈表的頭指針。實際的值就存儲在鏈表中算法
/** 單向鏈表的節點的結構體 */ typedef struct _htItem{ struct _htItem *next; char *key_string; uint fid; } htItem; /** 取得Key在數組中的映射位置 */ uint htIndex( char *key, htItem **ht ){ uint hashedKey = bkdrHash( key ); uint length = (ht[0]->fid-1); return (uint)hashedKey % length+1; } /** 定義數組 */ htItem *item[21]; //爲方便後臺實現,在定義在數組的時候,咱們給多一個長度,第一個數據不存儲數據,只存放數組長度,剩餘的元素空間
/** 有必要對數組元素進行一次0值 初始化 */ void htInit( htItem **ht, uint length ){ int i; for( i=0; i<length; i++ ){ ht[i] = (htItem*)malloc(sizeof(htItem)); memset( ht[i], 0, sizeof(htItem) ); } ht[0]->fid = length; }
首先經過哈希函數找到Key對應在數組中的位置,再遍歷鏈表,若是在鏈表中查到相同的key,就直接把該節點的數據更新,不然malloC一個節點,並把節點放到鏈表末尾。數組
/** set hashTable element */ uint htSet( char *key, uint fid, htItem **ht ){ uint i = htIndex( key, ht ); htItem *item = ht[i]; while( item->next ) { if( strcmp(key, item->next->key_string) == 0 ){ item->next->fid = fid; return 0; }else{ item = item->next; } } item->next = (htItem*)malloc(sizeof(htItem)); item->next->fid = fid; item->next->key_string = key; return 0; }
首先經過哈希函數定位KEY在數組中的位置,再依次對比KEY值,若是命中就返回相應的值,不然返回空值數據結構
/** get hashTable elements */ htItem* htGet( char *key, htItem **ht ){ uint i = htIndex( key, ht); htItem *item = ht[i]->next; htItem *tmp = (htItem*)malloc(sizeof(htItem)); memset(tmp, 0, sizeof(htItem)); while( item ) { if( strcmp(key, item->key_string) == 0 ){ printf("hiting %s\n", key); tmp->fid = item->fid; tmp->key_string = item->key_string; return tmp; } item = item->next; } return; }
/** delete one element of hashtable */ int htDel(char *key, htItem **ht){ uint i = htIndex(key, ht); htItem *item = ht[i]; while(item->next){ if(strcmp(key, item->next->key_string) == 0 ){ htItem *tmp = item->next; item->next = tmp->next; free(tmp); return 0; } item = item->next; } return -1; }
void print_hashTable( htItem **ht ) { uint length = ht[0]->fid; uint i; htItem *item; for( i = 1; i < length; i++ ) { item = ht[i]->next; while(item) { printf("%s => %d\n",item->key_string, item->fid); item = item->next; } } }
簡單幾個方法就實現了哈希表的增刪查改。函數
/** 定義與初始化 */ htItem *item[101]; htInit(item, 101); /** 添加元素 */ htSet("longmon",100,item); htSet("natalie", 1000,item); htSet("xiaoqiong",99, item); /** 打印⑴ */ print_hashTable(item); /** 刪除一個元素 */ htDel("natalie", item); /** 打印⑵ */ print_hashTable(item); /** 獲取key爲longmon的值 */ htItem *tmpitem = htGet("longmon", item); /** 打印⑶ */ printf("key: %s => val:%d\n", tmpitem->key_string, tmpitem->fid);
輸出結果ui
#打印⑴ xiaoqiong => 99 natalie => 1000 longmon => 100 #打印⑵ xiaoqiong => 99 longmon => 100 #打印⑶ Key: longmon => val: 100
PHP內核中的哈希表是十分重要的數據結構,PHP的大部分的語言特性都是基於哈希表實現的, 例如:變量的做用域、函數表、類的屬性、方法等,Zend引擎內部的不少數據都是保存在哈希表中的。而PHP內核中的哈希表處理衝突使用的也是拉鍊法。
擴展閱讀: