1. 程式人生 > 其它 >c++目錄項 雜湊表_第八章 雜湊表(三)

c++目錄項 雜湊表_第八章 雜湊表(三)

技術標籤:c++目錄項 雜湊表

圖形演示:雜湊表 這是圖形示例8-1,位於CD目錄:\demonstrations\ch08\Demo01 - Hash Tables\。 演示編譯 這個演示使用了SDLGUI庫,我已經編譯了,更多的關於這個庫的資訊,可以參考附錄B,“計算機程式的記憶體設計”。 要編譯這個演示程式,要麼開啟目錄中的工作區域的檔案,要麼使用附錄B中描述的設定建立自己的專案。如果建立自己的專案,需要包含目錄中的所有檔案。 在這本書的其他章節中,我通常把圖形演示放在本章前面。然而,我覺得在給你看一個圖形演示之前,我需要建立鏈溢位雜湊表。為了準備連結串列,我只向你展示了原始雜湊表型別,以便你理解它們背後的概念。然而,現實生活,我不會使用任何東西,除了鏈雜湊表。 這個示例將向你展示一個鏈式雜湊表內部工作。圖8.7展示了示例執行的螢幕截圖。

圖8.7 這是圖形演示8-1的螢幕截圖 表8.1顯示了這個示例效果的命令列表

表8.1 圖形示例8-1命令

按鈕 效果

插入(Insert) 此按鈕嘗試將文字框中的數字插入雜湊表

查詢(Find) 此按鈕在雜湊表裡查詢給定的鍵值

移除(Remove) 此按鈕從雜湊表移除給定的鍵值項

隨機(Random) 此按鈕放入一個隨機數到盒子裡

文字框 這裡你可以輸入數字來插入,搜尋或者移除

這是示例展示一個10個單元雜湊表,到目前為止,我在整個章節中都使用相同的方式。雜湊表被設計用於儲存三位整數,每個整數都是自己的鍵。事實上,鍵和儲存的資料不必相同,這我稍後再談。為了簡單起見,表中儲存每個數字都是它自己的鍵值。 圖8.7展示了雜湊表我已經插入了10個隨機數。注意任何單元包含的數字最多三個(在單元格6).這意味著為了找到給定的鍵是否包含在特定的雜湊表中,你最多作三次比較。 實現一個雜湊表 現在最終到了建立雜湊表的時候了。本節程式碼包含在CD \structures\HashTable.h檔案中。如我之前所說,有兩個資料與雜湊表相關:一個是鍵另一個是進入表的資料。 資料的鍵必須與它關聯的資料是唯一的。例如,如果你生活在美國,每個人都被分發了社會安全碼。如果你把人放進一個雜湊表,這將會是一個很好的關聯。因為社會安全碼是唯一的,沒有人有相同的社會安全碼。 當你在雜湊表裡放入資料,你輸入了與資料相關的資料和鍵值。雜湊表將儲存鍵和資料。當你想在雜湊表裡搜尋資料,你告訴表你要找的鍵,表就會返回資料,如果資料存在的話。 HashEntry類 因為雜湊表需要為插入的每個項儲存兩條資訊,建立包含兩部分資料的類是最簡單的。因為這兩個資料可以是不同的型別,HashEntry類將有兩個模板引數:
template< class KeyType, class DataType >    class HashEntry    {public:      KeyType m_key;              DataType m_data;    };
這兩個模板引數為KeyType和DataType。 HashTable 類 HashTable類將和HashEntry類一樣有相同的兩個模板引數。表8-2展示了HashTable類將支援的函式列表。

表8.2 hashTable函式

函式名 作用

Constructor 建立雜湊表提供固定大小雜湊函式

Insert 插入KeyType/DataType兩個到雜湊表

Find 在表中查詢給定的鍵並返回資料指標

Remove 表中找到給定的鍵,並移除與此關聯的資料

Count 返回進入表中的數量

資料 HashTable類將需要幾個成員變數。它需要追蹤表的大小,入表項數量,構成實際表的連結串列陣列,和一個指向雜湊函式的指標。如果你不熟悉函式指標,請閱讀附錄A,"C++ 入門",那裡有描述。
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);};
第5行typedef讓你生活感到輕鬆。沒有這個typedef,你需要定義 HashEntry 當你想要使用HashEntry型別的時候,這將使得程式碼冗長且醜陋。typeof關鍵字濃縮到了只是Entry,節省了我們大量的定義並使得程式碼更容易閱讀。 大小(m_size)和數量(m_count)功能是明顯的。 第三個成員變數,m_table,是一個DLinkedLists陣列。數組裡的每一項每一個連結串列都包含Entrys。 第四個成員變數是雜湊函式的函式指標。雜湊函式以一個鍵作為引數,返回一個無符號的長整數。我用雜湊函式作為函式指標有幾個原因。 首先,給雜湊表一個獨立的雜湊函式是非常好的。它允許你在表中使用不同資料。一些雜湊表實現直接將雜湊函式構造在雜湊表中,這使得它有極大是限制。這種方法,你可以有兩個雜湊表來儲存鍵型別和資料型別,但是兩個表可以使用不同的雜湊函式。 其次,它很容易使得雜湊表指向雜湊函式,因此,雜湊表自動雜湊鍵傳遞到表中。這樣,使用者不必記住雜湊的鍵;他或者她可以直接把鍵傳入表中。 再次,你並不希望雜湊函式更改。如果使用者允許改變雜湊函式,雜湊表將變得毫無價值。例如,假設使用者插入鍵值到表中使用一個雜湊函式。這時候,使用者改變雜湊雜湊函式,並設法用相同的鍵來搜尋。如果新的雜湊函式雜湊的鍵是一個不同的數字,那麼表將不會找的這個資料,即使它在表中! 稍後你將看到雜湊函式的指標是如何工作的。 建構函式 HashTable的建構函式將獲得兩個引數:表的大小和指向雜湊函式的指標。
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;}