簡體   English   中英

設計一個Hashtable

[英]Design a Hashtable

我在面試中被問到這個問題並且被遺忘了,盡管我想出了一個答案我對我的解決方案感到不舒服。 我想看看這里的專家對這個問題的看法。

我正在引用面試官的問題。 “設計一個哈希表,你可以使用你想要的任何數據結構。我想看看你如何實現O(1)查找時間”。 最后他說,這更像是通過另一個數據結構模擬哈希表。

有關這個問題的更多信息,任何人都可以點亮我。 謝謝!

PS:我提出這個問題的主要原因是要知道專家設計師如何從這個問題的設計開始&&還有一件事,我根據提出的其他問題以某種方式清除了采訪,但這個問題在我腦海中,我想找出答案!

這是一個相當標准的面試問題,它表明你理解了基礎概念是有用的Java數據結構,比如HashSetHashMap

您將使用列表數組,這些列表通常稱為存儲桶 您以給定容量n開始哈希表,這意味着您有一個包含10個列表的數組(全部為空)。

要向hastable中添加一個對象,可以調用對象hashCode函數,該函數為您提供一個int(一個相當大的范圍內的數字)。 因此,您必須將hashCode wrt模數為n,以便為其提供存儲的存儲桶。將對象添加到該存儲桶中列表的末尾。

要查找對象,請再次使用hashCode和mod函數查找存儲桶,然后使用.equals()遍歷列表以查找正確的對象。

隨着表格越來越豐富,您最終會進行越來越多的線性搜索,因此您最終需要重新哈希。 這意味着構建一個全新的,更大的表並再次將對象放入其中。

如果您想要的那個位置已滿,則可以重新計算不同的鏟斗位置,而不是在每個陣列位置使用List,常見的方法是二次探測 這樣做的優點是不需要任何動態數據結構,如列表,但更復雜。

您需要一組列表或值“桶”。 然后使用哈希函數確定要查看的數組元素,最后通過列表元素進行線性搜索。

您可以不斷查找數組位置,並在那里的小列表中對哈希值進行線性搜索。

如果我願意,我應該做到以下幾點:

  • 討論確切的哈希表是什么以及它應該在什么情況下使用。
  • 從消費者的角度討論其中一個實現(例如.net框架實現)。
  • 與面試官討論“HashTable如何在內部運作”。 這是非常重要的。 只有了解哈希表的工作原理,您才能設計它。
  • 打破問題:a。數據結構的選擇b。哈希函數的選擇
  • 使用TDD(測試驅動開發)來設計和實現HashTable類。 僅實現您要求的功能。

考慮Universe U(例如所有可能的IP地址,或所有可能的名稱或所有可能的移動號碼或所有可能的棋盤配置)。 您可能已經注意到宇宙U非常大。

Set S的大小合理S⊆U。因此,這套S的大小合理,就像你保留朋友的電話號碼一樣。

選擇實現數據結構沒有數據結構,我們將無法獲得良好的解決方案。 我們可以使用數組進行快速插入,刪除和查找,但由於Universe的大小非常大,它會占用大量空間。 此外,您的朋友名稱應為整數,空間要求與Universe成正比。

另一方面,我們可以使用鏈表。 這只會占用空間,因為有對象即Set S,但3次操作不會是O(1)。 要解決這個問題,我們可以使用兩者。

因此,解決方案是使用兩全其美,即快速查找數組和小型存儲大小,如鏈接列表。

但是,這些真實世界實體需要通過稱為哈希函數的東西更改為整數,以便它們可以用作數組索引。 所以,假設您想保存朋友的名字alice,只需將他的名字轉換為整數即可

插入愛麗絲:
int k = hashFunc(alice);
arr[k] = Alice //this takes O(1) time

查找alice:
int k = hashFunc(alice);
string name = arr[k] ;
print name;//prints alice

當然,這並不是那么簡單,但這是我現在可以解釋的。 如果我不清楚,請告訴我。謝謝。 有關哈希表的更多信息,請參閱此處

哈希表提供了一種有效插入和檢索數據的方法(通常在常量/ O(1))時間。 為此,我們使用一個非常大的數組來存儲目標值和一個通常映射目標值的哈希函數,這些哈希值只是這個大數組中的有效索引。 完美地散列值以存儲到唯一鍵(或表中的索引)的散列函數被稱為完美散列函數。 但實際上,為了存儲沒有已知方法來獲取唯一哈希值(表中的索引)的值,我們通常使用哈希函數,該函數可以將每個值映射到特定索引,以便可以將沖突保持為最小。 這里碰撞意味着要存儲在散列表中的兩個或更多個項映射到相同的散列值。

現在來看原始問題,即:“設計一個哈希表,你可以使用你想要的任何數據結構。我想看看你如何實現O(1)查找時間”。 最后他說,這更像是通過另一個數據結構模擬哈希表。“

如果我們可以設計一個完美的哈希函數,那么在O(1)時間內查找是可能的。 底層數據結構仍然是一個數組。 但它取決於要存儲的值,我們是否可以設計完美的哈希函數。 例如,將字符串視為英文字母。 由於沒有已知的散列函數可以將每個有效的英語單詞映射到唯一的int(32位)(或long long int 64 bit)值,因此總會有一些沖突。 為了處理沖突,我們可以使用沖突處理的單獨鏈接方法,其中每個哈希表槽存儲指向鏈表的指針,該鏈表實際上存儲了對該特定槽或索引的所有項哈希。 例如,考慮一個哈希函數,它將每個英文字母字符串視為基數為26的數字(因為英文字母中有26個字符),這可以編碼為:

unsigned int hash(const std::string& word)
{
    std::transform(word.begin(), word.end(), word.begin(), ::tolower);
    unsigned int key=0;
    for(int i=0;i<word.length();++i)
    {
         key = (key<<4) + (key<<3)+(key<<2) + word[i];
         key = key% tableSize;
    }
    return key;
}

其中tableSize是適當選擇的素數,其大於目標存儲在哈希表中的英語詞典單詞的總數。

以下是字典大小為144554的結果,以及大小= 144563的表:

[映射到相同單元格的項目 - >哈希表中此類插槽的數量] =======>

[ 0  -->   53278 ]
[1 --> 52962 ]
[2 --> 26833 ]
[3 --> 8653  ]
[4 --> 2313 ]
[5 --> 437 ]
[6  --> 78 ]
[7  -->  9 ]

在這種情況下,要搜索已映射到僅包含一個項目的單元格的項目,查找將為O(1),但如果它映射到具有多個項目的單元格,則我們必須遍歷此鏈接列表可能包含2到7個節點,然后我們將能夠找到該元素。 所以在這種情況下它不是常數。

所以它只取決於完美散列函數的可用性,我們是否可以在O(1)約束中執行查找。 否則它不會完全是O(1)但非常接近它。

使用數組=> O(1)

因此,您可以使用哈希函數將鍵轉換為數字,然后使用該數字作為數組的索引來檢索值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM