第五章_散列

來源:互聯網
上載者:User

散列表ADT,只支援二叉樹尋找所允許的一部分操作。比如插入,刪除,尋找等。那些需要元素間排序資訊的操作將不會得到支援。比如FindMin,FindMax和線性時間按排序次序列印整個表的操作時不支援的。

 

散列函數在理想狀態是能將任何兩個不同的關鍵字映射到不同的單元,但是這是不可能,因為關鍵字是無窮的,但是散列表是有大小的。所以我們要找的散列函數應該能夠在單元之間均勻的分配關鍵字。而且需要能夠解決衝突。

 

通常保證散列表的大小是素數。

 

解決衝突的方法:分離連結法和開放定址法

 

分離連結法

將散列到同一個值的所有元素儲存在一個鏈表中

 

#ifndef HASH_SEP_H#define HASH_SEP_Htypedef int ElementType;struct ListNode;typedef struct ListNode *Position;typedef Position List;struct HashTbl;typedef struct HashTbl *HashTable;HashTable InitializeTable(int TableSize);Position Find(ElementType key, HashTable H);void Insert(ElementType key, HashTable H);void PrintHashSep(HashTable H);#endif

 

#include "HashSep.h"#include <iostream>using namespace std;struct ListNode {ElementType Element;Position next;};struct HashTbl {int TableSize;List *TheLists;};int Hash(int n, int TableSize){return n %TableSize;}bool IsPrime(int n){if (2==n || 3==n)return true;if (1==n || (n&1)==0)return false;for (int i=3; i*i<=n; i+=2) {if (n%i==0)return false;}return true;}int NextPrime(int n){if (2==n)return n;if ((n&1)==0)++n;while (1) {if (IsPrime(n))return n;elsen += 2;}}HashTable InitializeTable(int TableSize){HashTable H;H = (HashTable)malloc(sizeof(struct HashTbl));if (H==NULL) {cout<<"out of space"<<endl;return NULL;}H->TableSize = NextPrime(TableSize);H->TheLists = (List*)malloc(sizeof(List)*(H->TableSize));if (H->TheLists==NULL) {cout<<"out of space"<<endl;return NULL;}for (int i=0; i<H->TableSize; ++i) {H->TheLists[i] = (List)malloc(sizeof(struct ListNode));if (H->TheLists[i]==NULL) {cout<<"out of space"<<endl;return NULL;}(H->TheLists[i])->next = NULL;}return H;}Position Find(ElementType key, HashTable H){Position P;List L;L = H->TheLists[Hash(key, H->TableSize)];P = L->next;while (P!=NULL && P->Element!=key)P = P->next;return P;}void Insert(ElementType key, HashTable H){Position pos = Find(key, H);Position newCell;List L;if (pos==NULL) {newCell = (Position)malloc(sizeof(struct ListNode));if (newCell==NULL) {cout<<"out of space"<<endl;return;} else {L = H->TheLists[Hash(key, H->TableSize)];newCell->next = L->next;newCell->Element = key;L->next = newCell;}}}  void PrintHashSep(HashTable H){List *L = H->TheLists;Position P;for (int i=0; i<H->TableSize; ++i) {P = L[i]->next;while (P!=NULL) {cout<<P->Element<<" ";P = P->next;}}}int main(int argc, char **argv){HashTable H = InitializeTable(6);for (int i=0; i<H->TableSize; ++i)Insert(i, H);Insert(13, H);Insert(16, H);Insert(9, H);    PrintHashSep(H);cout<<endl;if (Find(9, H)) {cout<<Find(9, H)->Element<<endl;}system("pause");return 0;}

 

 

裝填因子

定義裝填因子λ為散列表中元素個數與散列表大小的比值。

 

開放定址法

不需要用鏈表解決衝突。在開放定址散列法中,當發現衝突就嘗試選擇另外的單元,直到找出空的單元。更一般的,單元h0(x), h1(x),h2(x)…相繼被試選,其中hi(x) = (Hash(x)+F[i]) mod TableSize。因為所有的資料都要放入表內,所以開放定址法比分離連結法所需要的散列表大。一般對於開放定址散列演算法,裝填因子λ應該低於0.5。

 

線性探測法

函數F是i的線性函數,典型的情況是F(i) = i。即逐個探測每個單元以尋找一個空單元。容易產生一次聚集效應。

 

如果一個表可以有多於一半被填滿的話,那麼線性探測就不是個好辦法。

 

平方探測法

可以消除線性探測中的一次聚集問題。F(i) = i^2。

對於平方探測,如果表的大小是素數,當表至少還有一半是空的時候,總能插入一個新元素。一旦表被填滿超過一半,當表的大小不是素數時甚至在表被填滿之前,就不能保證一次找到一個空單元了。

 

哪怕表有比一半多一個的位置被填滿,插入都有可能失敗。表的大小是素數也很重要,因為若不是素數,備選單元的個數可能會銳減。

 

在開放定址散列表中,標準的刪除操作不能進行,因為響應的單元可能已經引起過衝突,元素繞過它存到了別處。開放定址散列表需要懶惰刪除。

 

散列到同一位置上的哪些元素將探測相同的備選單元,這叫做二次聚集。

 

 

#ifndef _HASH_QUAD_H#define _HASH_QUAD_Htypedef int ElementType;typedef unsigned int Index;typedef Index Position;typedef struct HashEntry Cell;typedef struct HashTbl *HashTable;HashTable InitializeTable(int TableSize);Position Find(ElementType Key, HashTable H);void Insert(ElementType Key, HashTable H);#endif

 

#include "HashQuad.h"#include <iostream>using namespace std;enum KindOfEntry {Legitimate,Empty,Deleted};struct HashEntry {ElementType Element;enum KindOfEntry Info;};struct HashTbl {int TableSize;Cell *TheCells;};bool IsPrime(int n){if (2==n || 3==n)return true;if (1==n || (n&1)==0)return false;for (int i=3; i*i<=n; i+=2) {if (n%i==0)return false;}return true;}int NextPrime(int n){if (2==n)return n;if ((n&1)==0)++n;while (1) {if (IsPrime(n))return n;elsen += 2;}}int Hash(int key, int TableSize) {return key%TableSize ;}HashTable InitializeTable(int TableSize){HashTable H = (HashTable)malloc(sizeof(struct HashTbl));if (H==NULL) {cout<<"out of space"<<endl;return NULL;}H->TableSize = NextPrime(TableSize);H->TheCells = (Cell*)malloc(sizeof(Cell)*H->TableSize);if (H->TheCells==NULL) {cout<<"out of space"<<endl;return NULL;}for (int i=0; i<H->TableSize; ++i)H->TheCells[i].Info = Empty;return H;}Position Find(ElementType Key, HashTable H){Position CurrentPos;int CollisionNum = 0;CurrentPos = Hash(Key, H->TableSize);while (H->TheCells[CurrentPos].Info!=Empty &&H->TheCells[CurrentPos].Element!=Key) {CurrentPos += 2 * ++CollisionNum - 1;if (CurrentPos>=H->TableSize)CurrentPos -= H->TableSize;}return CurrentPos;}void Insert(ElementType Key, HashTable H){Position Pos;Pos = Find(Key, H);if (H->TheCells[Pos].Info!=Legitimate) {H->TheCells[Pos].Info = Legitimate;H->TheCells[Pos].Element = Key;}}int main(int argc, char **argv){system("pause");return 0;}

 

 

雙散列

對於雙散列,一種流行的選擇是F(i) = I * hash2(X)。我們將第二個散列函數應用到X並在距離hash2(X),2hash2(X)處探測。注意,函數一定不要算得0值。

 

再散列

如果表填的太滿,操作的已耗用時間將開始消耗過長,且Insert操作可能失敗。一種解決方案是建立另外一個大約兩倍大的表(而且使用一個相關的新散列函數),掃描整個原始散列表,計算每個(未刪除的)元素的新散列值並將其插入到新表中。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.