《資料結構與演算法C#語言描述》筆記10_雜湊和Hashtable類
雜湊概述
儲存在陣列內的每一個數據項都是基於一些資料塊,這資料塊被稱為鍵。
把鍵對映到一個範圍從0到雜湊大小的數上,這需要雜湊函式完成。
雜湊函式的理想目標是把自身單元內的每一個鍵都儲存到陣列內,但可能會出現兩個鍵雜湊到相同數值的情況,這現象叫做衝突。
雜湊的主要問題是:
1)確定用多大維數的陣列作為散列表?原則:建議陣列的大小是一個素數
2)如何選擇雜湊函式?根據所用鍵的資料型別,把鍵儘可能平均地分不到陣列的單元內。
3)如何解決衝突?確定適當陣列大小的策略,基於如何解決衝突。
散列表中元素數量與表大小的比率被稱為負載係數。負載係數為1.0時,散列表的效能最佳。
選擇陣列大小的時候,一個重要的原則就是要選擇素數。
10007是素數,而且他沒有大到會使用大量的記憶體來降低程式的記憶體。
下面例子中,雜湊函式SimpleHash利用霍納(Horner)法則來計算(關於37的)多項式函式。
string[] names = new string[10007];
string name;
string[] somenames = new string[]
{ "David", "Jennifer", "Donnie","Mayo",
"Raymond","Bernica","Mike", "Clayton"
int hashVal;
for (inti = 0; i < 10; i++)
{
name = somenames[i];
hashVal = SimpleHash(name, names);
names[hashVal] = name;
}
ShowDistrib(names);
///337 Bernica
///
///4395 Donnie
///4929 Mayo
///4943Jennifer
///5219 Mike
///8094 Raymond
///8547Michael
///8942 Beata
///9237Clayton
private intSimpleHash(string s, string[]arr)
{
int tot = 0;
char[] cname;
cname = s.ToCharArray();
for (inti = 0; i < cname.GetUpperBound(0); i++)
{
tot += 37 * tot + (int)cname[i];
}
tot = tot % arr.GetUpperBound(0);
if (tot < 0)
{ tot += arr.GetUpperBound(0); }
return (int)tot;
}
static voidShowDistrib(string[] arr)
{
for (inti = 0; i < arr.GetUpperBound(0); i++)
{
if (arr[i] != null)
{ Console.WriteLine(i+" "+arr[i]); }
}
}
在散列表中查詢資料,需要計算鍵的雜湊值,然後訪問陣列中的對應元素。
這樣的查詢方式很明顯要比逐個查詢要效率的多。
解決衝突
桶式雜湊法
桶,是一種儲存在散列表元素內的簡單資料結構 ,它可以儲存多個數據項。大多數實現中,這種資料結構就是一個數組,例如ArrayList類。
桶式雜湊法,最終要的事情,就是保持所用陣列的數量儘可能地少。
用雜湊函式來確認用哪個陣列(雜湊鍵)來儲存資料項,然後檢視此資料項是否已經在陣列內。如果存在,就不做。如果不存在,就將資料項新增進這個陣列。
用雜湊函式來確認用哪個陣列(雜湊鍵)來移出資料項,然後檢視此資料項是否已經在陣列內。如果存在,就移出資料項。如果不存在,就不做。
開放定址法
開放定址函式會在散列表陣列內尋找空單元來防止資料項。
兩種不同的開放定址策略:
線性探查
採用線性函式來確認試圖插入的陣列單元。順次嘗試單元直到找到一個空單元為止。
會出現的問題是陣列內相鄰單元中的資料元素會趨近成聚類,從而使得後續空單元的探查時間變得更長且效率更低。
平方探查
平方函式來確認要嘗試哪個單元。
平方探查法的有趣屬性是在散列表空餘單元少於一半的情況下總能保證找到空的單元。
雙重雜湊法
兩個條件:雜湊函式不應該曾經計算到0,;表的大小必須是素數
常見的應用之一,就是構造術語表或者術語詞典。
可以為鍵的資料型別指定雜湊函式或者使用內建的函式。
避免衝突的策略是桶的思想。
namespace System.Collections
public class Hashtable : IDictionary,ICollection, IEnumerable,ISerializable, IDeserializationCallback,ICloneable
構造器
其中一個有代表性的宣告方式:
//
// 摘要:
// 使用指定的初始容量、指定的載入因子、預設的雜湊程式碼提供程式和預設比較器來初始化 System.Collections.Hashtable類的新的空例項。
//
// 引數:
// capacity:
// System.Collections.Hashtable物件最初可包含的元素的近似數目。
//
// loadFactor:
// 0.1 到 1.0 範圍內的數字,再乘以提供最佳效能的預設值。結果是元素與儲存桶的最大比率。
//
// 異常:
// System.ArgumentOutOfRangeException:
// capacity 小於零。- 或 -loadFactor小於 0.1。-或 -loadFactor 大於 1.0。
//
// System.ArgumentException:
// capacity 導致溢位。
public Hashtable(intcapacity,float loadFactor);
程式碼:
Hashtable g = new Hashtable();///預設容量,預設負載係數。capacity:0.72
Hashtable g1 = new Hashtable(50);///50個元素容量,預設負載係數。capacity:0.72
Hashtable g2 = new Hashtable(25,0.3F);///25個元素且負載係數是0.3。capacity:0.216000021
Keys、Values方法返回一個Enumerator物件。
foreach (varitemin g.Keys)
{
}
foreach (varitemin g.Values)
{
}
對散列表使用索引,索引為鍵名。
foreach (varitemin g.Keys)
{
Console.Write(g[item]);
}
Count屬性,散列表內元素的數量
Clear方法,清除散列表內容的方法
ContainKey方法(ContainValue方法),判斷散列表內是否含指定鍵(和數值)的方法
Remove方法,從散列表中移除元素
CopyTo方法,散列表元素賦值到陣列