備戰秋招——算法與數據結構(5)

在這裏插入圖片描述

● 請你來說一說hash表的實現,包括STL中的哈希桶長度常數

參考回答:
hash表的實現主要包括構造哈希和處理哈希衝突兩個方面:
對於構造哈希來說,主要包括直接地址法、平方取中法、除留餘數

法等。

對於處理哈希衝突來說,最常用的處理衝突的方法有開放定址法、再哈希法、鏈地址法、建立公共溢出區等方法。SGL版本使用鏈地址法,使用一個鏈表保持相同散列值的元素。

雖然鏈地址法並不要求哈希桶長度必須爲質數,但SGI STL仍然以質數來設計哈希桶長度,並且將28個質數(逐漸呈現大約兩倍的關係)計算好,以備隨時訪問,同時提供一個函數,用來查詢在這28個質數之中,“最接近某數並大於某數”的質數。

● 請你回答一下hash表如何rehash,以及怎麼處理其中保存的資源

參考回答:
C++的hash表中有一個負載因子loadFactor,當loadFactor<=1時,hash表查找的期望複雜度爲O(1). 因此,每次往hash表中添加元素時,我們必須保證是在loadFactor <1的情況下,才能夠添加。
因此,當Hash表中loadFactor==1時,Hash就需要進行rehash。rehash過程中,會模仿C++的vector擴容方式,Hash表中每次發現loadFactor ==1時,就開闢一個原來桶數組的兩倍空間,稱爲新桶數組,然後把原來的桶數組中元素全部重新哈希到新的桶數組中。

● 請你說一下哈希表的桶個數爲什麼是質數,合數有何不妥?

參考回答:
哈希表的桶個數使用質數,可以最大程度減少衝突概率,使哈希後的數據分佈的更加均勻。如果使用合數,可能會造成很多數據分佈會集中在某些點上,從而影響哈希表效率。
算法:

給定一個數字數組,返回哈夫曼樹的頭指針

struct BTreeNode* CreateHuffman(ElemType a[], int n)
{
int i, j;
struct BTreeNode **b, *q;
b = malloc(n*sizeof(struct BTreeNode));
for (i = 0; i < n; i++)
{
b[i] = malloc(sizeof(struct BTreeNode));
b[i]->data = a[i];
b[i]->left = b[i]->right = NULL;
}
for (i = 1; i < n; i++)
{
int k1 = -1, k2;
for (j = 0; j < n; j++)
{
if (b[j] != NULL && k1 == -1)
{
k1 = j;
continue;
}
if (b[j] != NULL)
{
k2 = j;
break;
}
}
for (j = k2; j < n; j++)
{
if (b[j] != NULL)
{
if (b[j]->data < b[k1]->data)
{
k2 = k1;
k1 = j;
}
else if (b[j]->data < b[k2]->data)
k2 = j;
}
}
q = malloc(sizeof(struct BTreeNode));
q->data = b[k1]->data + b[k2]->data;
q->left = b[k1];
q->right = b[k2];
b[k1] = q;
b[k2] = NULL;
}
free(b);
return q;
}

● 請你說一下解決hash衝突的方法

參考回答:
當哈希表關鍵字集合很大時,關鍵字值不同的元素可能會映象到哈希表的同一地址上,這樣的現象稱爲哈希衝突。目前常用的解決哈希衝突的方法如下:
開放定址法: 當發生地址衝突時,按照某種方法繼續探測哈希表中的其他存儲單元,直到找到空位置爲止。

再哈希法:當發生哈希衝突時使用另一個哈希函數計算地址值,直到衝突不再發生。這種方法不易產生聚集,但是增加計算時間,同時需要準備許多哈希函數。

鏈地址法:將所有哈希值相同的Key通過鏈表存儲。key按順序插入到鏈表中

建立公共溢出區:採用一個溢出表存儲產生衝突的關鍵字。如果公共溢出區還產生衝突,再採用處理衝突方法處理。

● 請你說一說哈希衝突的解決方法

參考回答:
考察點:hash衝突,數據結構
公司:騰訊

1、開放定址

開放地址法有個非常關鍵的特徵,就是所有輸入的元素全部存放在哈希表裏,也就是說,位桶的實現是不需要任何的鏈表來實現的,換句話說,也就是這個哈希表的裝載因子不會超過1。它的實現是在插入一個元素的時候,先通過哈希函數進行判斷,若是發生哈希衝突,就以當前地址爲基準,根據再尋址的方法(探查序列),去尋找下一個地址,若發生衝突再去尋找,直至找到一個爲空的地址爲止。所以這種方法又稱爲再散列法。

有幾種常用的探查序列的方法:

①線性探查

dii=1,2,3,…,m-1;這種方法的特點是:衝突發生時,順序查看錶中下一單元,直到找出一個空單元或查遍全表。

②二次探查

di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 );這種方法的特點是:衝突發生時,在表的左右進行跳躍式探測,比較靈活。

③ 僞隨機探測

di=僞隨機數序列;具體實現時,應建立一個僞隨機數發生器,(如i=(i+p) % m),生成一個位隨機序列,並給定一個隨機數做起點,每次去加上這個僞隨機數++就可以了。

2、鏈地址

每個位桶實現的時候,採用鏈表或者樹的數據結構來去存取發生哈希衝突的輸入域的關鍵字,也就是被哈希函數映射到同一個位桶上的關鍵字。
在這裏插入圖片描述
紫色部分即代表哈希表,也稱爲哈希數組,數組的每個元素都是一個單鏈表的頭節點,鏈表是用來解決衝突的,如果不同的key映射到了數組的同一位置處,就將其放入單鏈表中,即鏈接在桶後。

3、公共溢出區

建立一個公共溢出區域,把hash衝突的元素都放在該溢出區裏。查找時,如果發現hash表中對應桶裏存在其他元素,還需要在公共溢出區裏再次進行查找。

4、再hash

再散列法其實很簡單,就是再使用哈希函數去散列一個輸入的時候,輸出是同一個位置就再次散列,直至不發生衝突位置。

缺點:每次衝突都要重新散列,計算時間增加。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章