哈希(一)闭散列法
哈希(一)閉散列法
哈希概念
在二叉樹搜索中,我們總是要對數(shù)據(jù)進(jìn)行排序然后在根據(jù)排序結(jié)果進(jìn)行查找。然而對于某些場景來說,總是要進(jìn)行多次比較才可以搜索到數(shù)據(jù),這樣復(fù)雜度較高,較為復(fù)雜。于是我們想出了一種新的方法根據(jù)關(guān)鍵碼進(jìn)行映射查找類似于我們?nèi)杲钑鴷r(shí),店長有一個(gè)小本本記錄當(dāng)天借書與還書的情況來搜素?cái)?shù)據(jù)。于是我們將這種關(guān)鍵碼映射的方法稱作哈希結(jié)構(gòu)。
理想的搜索方式為我們進(jìn)行一次搜索便可以找到想要的數(shù)據(jù)。那么我們可以構(gòu)建一個(gè)類似于上面書店這個(gè)例子的賬本,每一頁存一本書的信息,提到書名我們便可以通過其中特殊的映射方式只尋找一次便找到賬本關(guān)于這本書的這一頁。我們將這種映射方式定義為HashFunc。
當(dāng)我們向該結(jié)構(gòu)中:
- 插入數(shù)據(jù)時(shí):根據(jù)該數(shù)據(jù)的關(guān)鍵碼算出插入位置,然后進(jìn)行插入。狀態(tài)置為存在。
- 刪除數(shù)據(jù)時(shí):根據(jù)該數(shù)據(jù)關(guān)鍵碼算出刪除位置,若位置已經(jīng)存在數(shù)據(jù)則為哈希沖突,重新映射插入數(shù)據(jù)。
現(xiàn)在讓我們總結(jié)下哈希結(jié)構(gòu)開散列的特點(diǎn):
數(shù)據(jù)通過映射插入哈希表中,每一個(gè)位置只可以存在一個(gè)數(shù)據(jù)。
我們用以下哈希表為例 :
映射方式為 :**key(數(shù)據(jù))%N(表容量)**除留余數(shù)法
假設(shè)我們插入 13,15
不過我們可以發(fā)現(xiàn),當(dāng)我們需要插入的數(shù)據(jù)變多時(shí),將會出現(xiàn)問題,例如當(dāng)我們插入24時(shí),映射位置也是2,那么會發(fā)生沖突,這就產(chǎn)生了一個(gè)新的概念**“哈希沖突”**。
哈希沖突
產(chǎn)生哈希沖突的本質(zhì)是由于一對一映射的時(shí)候,由于兩種數(shù)據(jù)產(chǎn)生了同一種映射導(dǎo)致位置不夠。那么我們可以將第二個(gè)數(shù)放在其他方便查找的位置。這里提供兩種方式解決哈希沖突。
查找數(shù)據(jù)時(shí)我們先找到映射位置,如果該數(shù)據(jù)與映射位置的數(shù)據(jù)不相等時(shí),我們在根據(jù)放置方式進(jìn)行再次查找即可。不過這樣又會產(chǎn)生新的問題,當(dāng)我們插入了11個(gè)數(shù)據(jù)時(shí),由于我們解決了哈希沖突每一個(gè)位置都插入了數(shù)據(jù),這樣我們在插入數(shù)據(jù)時(shí)將會無法插入。我們引入下一個(gè)概念,負(fù)載因子。
負(fù)載因子
我們定義負(fù)載因子為哈希表當(dāng)前數(shù)據(jù)除以哈希表容量,那么當(dāng)負(fù)載因子過大時(shí)將會產(chǎn)生很大的哈希沖突,我們找一個(gè)數(shù)將會無比麻煩,所以當(dāng)負(fù)載因子高于某一個(gè)值時(shí)我們要進(jìn)行擴(kuò)容操作。根據(jù)研究,閉散列的負(fù)載因子一般控制在0.7到0.8之間。例如JAVA的系統(tǒng)庫將負(fù)載因子設(shè)在了0.75.
可是我們擴(kuò)容來說又有新的問題,當(dāng)我們下一個(gè)容量選擇不恰當(dāng)?shù)脑?#xff0c;將會產(chǎn)生很多值映射在一個(gè)位置的尷尬情況,為了解決這個(gè)問題,我們每一次的擴(kuò)容都選擇質(zhì)數(shù),這里給出下面代碼實(shí)現(xiàn)所采用的素?cái)?shù)表。
代碼實(shí)現(xiàn)
下面給出哈希閉散列的代碼實(shí)現(xiàn)以供參考。
定義哈希表結(jié)構(gòu)體
typedef int KeyType; typedef int ValueType;enum Status {EMPTY,EXITS,_DELETE, };//每一個(gè)位置的狀態(tài)typedef struct HashNodeAZ {KeyType _key;ValueType _value;Status _status; }HashNode;//節(jié)點(diǎn)typedef struct HashTable {HashNode* _tables;size_t _size;size_t _N; }HashTable;擴(kuò)容用素?cái)?shù)表
size_t GetNextPrimeNum(size_t cur) {static int prime_array[] = {17,/* 0 */ 37, /* 1 */79,/* 2 */163,/* 3 */331,/* 4 */673,/* 5 */1361,/* 6 */2729,/* 7 */5471,/* 8 */10949,/* 9 */21911,/* 10 */43853,/* 11 */87719,/* 12 */175447,/* 13 */350899,/* 14 */};if (cur == 0){return prime_array[0];}for (int i = 0;i < 27;i++){if (cur == prime_array[i]){return prime_array[i + 1];}}return 0; }計(jì)算映射方法
size_t HashFunc(KeyType key, size_t N) {return key % N; }初始化
void HashTableInit(HashTable* ht) {ht->_N = GetNextPrimeNum(0);ht->_tables = (HashNode*)malloc(sizeof(HashNode)*ht->_N);ht->_size = 0;int i = 0;for (;i < ht->_N;i++){ht->_tables[i]._status = EMPTY;} }擴(kuò)容
void HashInsertCapacity(HashTable* ht) {if (((ht)->_size * 10) >((ht)->_N * 7)){HashTable newhash;newhash._N = GetNextPrimeNum(ht->_N);newhash._tables = (HashNode*)malloc(sizeof(HashNode)*newhash._N);for (int i = 0;i < newhash._N;i++){newhash._tables[i]._status = EMPTY;}newhash._size = 0;for (int j = 0;j < (ht)->_N;j++){if ((ht)->_tables[j]._status == EXITS){HashTableInsert(&newhash, ht->_tables[j]._key, ht->_tables[j]._value);}}HashTableDestory(ht);ht->_N = newhash._N;ht->_size = newhash._size;ht->_tables = newhash._tables;} }插入
int HashTableInsert(HashTable* ht, KeyType key, ValueType value) {HashInsertCapacity(ht);int i = HashFunc(key, ht->_N);while (ht->_tables[i]._status == EXITS){if (ht->_tables[i]._key == key){return -1;}if (i == ht->_N){i = 0;}i++;}ht->_tables[i]._key = key;ht->_tables[i]._value = value;ht->_tables[i]._status = EXITS;ht->_size++;return 0; }查找
HashNode* HashTableFind(HashTable* ht, KeyType key) {int i = HashFunc(key, ht->_N);while (ht->_tables[i]._status == EXITS){if (ht->_tables[i]._key == key){return &ht->_tables[i];}if (i == ht->_N){i = 0;}i++;}return NULL; }刪除
int HashTableRemove(HashTable* ht, KeyType key) {int i = HashFunc(key, ht->_N);while (ht->_tables[i]._status == EXITS){if (ht->_tables[i]._key == key){ht->_tables[i]._status = _DELETE;ht->_size--;return 0;}i++;}return -1; }打印哈希表
void HashPrint(HashTable* ht) {int i = 0;for (;i < ht->_N;i++){if (ht->_tables[i]._status == EMPTY){printf("[%d EMPTY] ", i);}if (ht->_tables[i]._status == EXITS){printf("[EXITS : %d ] ", ht->_tables[i]._key);}if (ht->_tables[i]._status == _DELETE){printf("[%d DELETE] ", i);}} }總結(jié)
- 上一篇: Excel两列求差集和并集的实现
- 下一篇: 公众号素材的运用:配置公众号菜单栏或者被