c++目錄項 雜湊表_第八章 雜湊表(三)
阿新 • • 發佈:2021-02-01
技術標籤:c++目錄項 雜湊表
圖形演示:雜湊表 這是圖形示例8-1,位於CD目錄:\demonstrations\ch08\Demo01 - Hash Tables\。 演示編譯 這個演示使用了SDLGUI庫,我已經編譯了,更多的關於這個庫的資訊,可以參考附錄B,“計算機程式的記憶體設計”。 要編譯這個演示程式,要麼開啟目錄中的工作區域的檔案,要麼使用附錄B中描述的設定建立自己的專案。如果建立自己的專案,需要包含目錄中的所有檔案。 在這本書的其他章節中,我通常把圖形演示放在本章前面。然而,我覺得在給你看一個圖形演示之前,我需要建立鏈溢位雜湊表。為了準備連結串列,我只向你展示了原始雜湊表型別,以便你理解它們背後的概念。然而,現實生活,我不會使用任何東西,除了鏈雜湊表。 這個示例將向你展示一個鏈式雜湊表內部工作。圖8.7展示了示例執行的螢幕截圖。表8.1 圖形示例8-1命令
按鈕 效果
插入(Insert) 此按鈕嘗試將文字框中的數字插入雜湊表
查詢(Find) 此按鈕在雜湊表裡查詢給定的鍵值
移除(Remove) 此按鈕從雜湊表移除給定的鍵值項
隨機(Random) 此按鈕放入一個隨機數到盒子裡
文字框 這裡你可以輸入數字來插入,搜尋或者移除
這是示例展示一個10個單元雜湊表,到目前為止,我在整個章節中都使用相同的方式。雜湊表被設計用於儲存三位整數,每個整數都是自己的鍵。事實上,鍵和儲存的資料不必相同,這我稍後再談。為了簡單起見,表中儲存每個數字都是它自己的鍵值。 圖8.7展示了雜湊表我已經插入了10個隨機數。注意任何單元包含的數字最多三個(在單元格6).這意味著為了找到給定的鍵是否包含在特定的雜湊表中,你最多作三次比較。 實現一個雜湊表 現在最終到了建立雜湊表的時候了。本節程式碼包含在CD \structures\HashTable.h檔案中。如我之前所說,有兩個資料與雜湊表相關:一個是鍵另一個是進入表的資料。 資料的鍵必須與它關聯的資料是唯一的。例如,如果你生活在美國,每個人都被分發了社會安全碼。如果你把人放進一個雜湊表,這將會是一個很好的關聯。因為社會安全碼是唯一的,沒有人有相同的社會安全碼。 當你在雜湊表裡放入資料,你輸入了與資料相關的資料和鍵值。雜湊表將儲存鍵和資料。當你想在雜湊表裡搜尋資料,你告訴表你要找的鍵,表就會返回資料,如果資料存在的話。 HashEntry類 因為雜湊表需要為插入的每個項儲存兩條資訊,建立包含兩部分資料的類是最簡單的。因為這兩個資料可以是不同的型別,HashEntry類將有兩個模板引數:這兩個模板引數為KeyType和DataType。 HashTable 類 HashTable類將和HashEntry類一樣有相同的兩個模板引數。表8-2展示了HashTable類將支援的函式列表。template< class KeyType, class DataType > class HashEntry {public: KeyType m_key; DataType m_data; };
表8.2 hashTable函式
函式名 作用
Constructor 建立雜湊表提供固定大小雜湊函式
Insert 插入KeyType/DataType兩個到雜湊表
Find 在表中查詢給定的鍵並返回資料指標
Remove 表中找到給定的鍵,並移除與此關聯的資料
Count 返回進入表中的數量
資料 HashTable類將需要幾個成員變數。它需要追蹤表的大小,入表項數量,構成實際表的連結串列陣列,和一個指向雜湊函式的指標。如果你不熟悉函式指標,請閱讀附錄A,"C++ 入門",那裡有描述。第5行typedef讓你生活感到輕鬆。沒有這個typedef,你需要定義 HashEntry 當你想要使用HashEntry型別的時候,這將使得程式碼冗長且醜陋。typeof關鍵字濃縮到了只是Entry,節省了我們大量的定義並使得程式碼更容易閱讀。 大小(m_size)和數量(m_count)功能是明顯的。 第三個成員變數,m_table,是一個DLinkedLists陣列。數組裡的每一項每一個連結串列都包含Entrys。 第四個成員變數是雜湊函式的函式指標。雜湊函式以一個鍵作為引數,返回一個無符號的長整數。我用雜湊函式作為函式指標有幾個原因。 首先,給雜湊表一個獨立的雜湊函式是非常好的。它允許你在表中使用不同資料。一些雜湊表實現直接將雜湊函式構造在雜湊表中,這使得它有極大是限制。這種方法,你可以有兩個雜湊表來儲存鍵型別和資料型別,但是兩個表可以使用不同的雜湊函式。 其次,它很容易使得雜湊表指向雜湊函式,因此,雜湊表自動雜湊鍵傳遞到表中。這樣,使用者不必記住雜湊的鍵;他或者她可以直接把鍵傳入表中。 再次,你並不希望雜湊函式更改。如果使用者允許改變雜湊函式,雜湊表將變得毫無價值。例如,假設使用者插入鍵值到表中使用一個雜湊函式。這時候,使用者改變雜湊雜湊函式,並設法用相同的鍵來搜尋。如果新的雜湊函式雜湊的鍵是一個不同的數字,那麼表將不會找的這個資料,即使它在表中! 稍後你將看到雜湊函式的指標是如何工作的。 建構函式 HashTable的建構函式將獲得兩個引數:表的大小和指向雜湊函式的指標。template< class KeyType, class DataType >class HashTable{ public: typedef HashEntry Entry; int m_size; int m_count; Array< DLinkedList< Entry > > m_table; unsigned long int (*m_hash)(KeyType);};
HashTable( int p_size, unsigned long int (*p_hash)(KeyType) ): m_table( p_size ){ // set the size, hash function, and count. m_size = p_size; m_hash = p_hash; m_count = 0;}
在程式碼第二行,我使用了標準C++構造標記法來呼叫m_table建構函式,因此,它將初始化正確的大小。如果你不熟悉標記法,請閱讀附錄A。那裡我有解釋。
插入函式
如我之前描述的,Insert函式將獲得一個鍵值對插入到表中。
void Insert( KeyType p_key, DataType p_data ){ Entry entry; entry.m_data = p_data; entry.m_key = p_key; int index = m_hash( p_key ) % m_size; m_table[index].Append( entry ); m_count++;}
首先,Entry結構體被建立,並將鍵和資料傳入。
然後,m_hash函式指標在傳入鍵後被呼叫。因為函式支援返回無符號長整型,這個結果可能超出了表的範圍。因為這樣,結果使用modulo(模)函式修正來讓它變成有效的表的下標。
最終,entry被附加到index指向的單元的連結串列中,並且增加資料數量。
注意
注意這個雜湊表本質上使用我之前描述的雙雜湊法。首先,鍵雜湊成一個整型,然後,整型再次使用模運算雜湊
查詢函式
此函式旨在搜尋雜湊表,以查看錶中是否有某個鍵。如果有,它將返回一個鍵所在entry結構體的指標。如果沒有,它將返回0。
Entry* Find( KeyType p_key ){ int index = m_hash( p_key ) % m_size; DListIterator itr = m_table[index].GetIterator(); while( itr.Valid() ) { if( itr.Item().m_key == p_key ) return &(itr.Item()); itr.Forth(); } return0;}
鍵雜湊到一個索引使用你插入鍵到表中完全相同的方法。這時,一個迭代器將被建立,它將指向被雜湊鍵的單元中的連結串列。
這時候迭代器遍歷整個連結串列,來檢查這些鍵是否匹配。如果匹配,entry指標將被返回。如果不匹配,將繼續迴圈。如果鍵不在表中,將被返回0。
移除函式
移除函式本質上和搜尋函式一樣,只是替換了返回entry的指標為entry從表中刪除。
這個函式返回一個布林值。true表示entry被找到並移除了;false意味著entry不存在。
bool Remove( KeyType p_key ){ int index = m_hash( p_key ) % m_size; DListIterator itr = m_table[index].GetIterator(); while( itr.Valid() ) { if( itr.Item().m_key == p_key ) { m_table[index].Remove( itr ); m_count—; return true; } itr.Forth(); } return false;}