散列表简介
散列表是一种常用的数据结构,用于存储键值对。它通过计算键的哈希值,将键值对存储在一个数组中,从而实现高效的数据访问。在C#中,我们可以使用内置的Dictionary
类来实现散列表。
散列函数
散列函数是将键映射到数组索引的函数。它的目标是尽可能地将不同的键映射到不同的索引,从而减少冲突。C#中的Dictionary
类使用默认的散列函数,但我们也可以自定义散列函数。
散列函数示例
public int GetHash(string key)
{
int hash = 0;
for (int i = 0; i < key.Length; i++)
{
hash += (int)key[i];
}
return hash % capacity;
}
上述代码示例中,我们将键中每个字符的ASCII值相加,然后对散列表的容量取模,得到对应的索引。
解决冲突
由于散列函数的映射并不是一对一的,所以可能会出现不同的键映射到相同的索引的情况,这就是冲突。为了解决冲突,常用的方法有链地址法和开放寻址法。
链地址法
链地址法是将相同索引的键值对存储在一个链表中。当发生冲突时,我们将新的键值对添加到链表的末尾。在访问散列表中的键值对时,我们首先计算键的哈希值,然后在对应索引的链表中查找。
链地址法示例
public class HashTable
{
private LinkedList<KeyValuePair<string, int>>[] table;
public HashTable(int capacity)
{
table = new LinkedList<KeyValuePair<string, int>>[capacity];
}
public void Add(string key, int value)
{
int index = GetHash(key);
if (table[index] == null)
{
table[index] = new LinkedList<KeyValuePair<string, int>>();
}
table[index].AddLast(new KeyValuePair<string, int>(key, value));
}
public int Get(string key)
{
int index = GetHash(key);
if (table[index] == null)
{
throw new KeyNotFoundException();
}
foreach (var pair in table[index])
{
if (pair.Key == key)
{
return pair.Value;
}
}
throw new KeyNotFoundException();
}
}
上述代码示例中,我们使用LinkedList
来实现链表,并将每个索引的键值对存储在对应的链表中。
开放寻址法
开放寻址法是将相同索引的键值对存储在散列表的其他位置。当发生冲突时,我们根据一定的规则,寻找下一个可用的位置。在访问散列表中的键值对时,我们根据规则依次查找,直到找到对应的键值对或者遇到空位置。
开放寻址法示例
public class HashTable
{
private KeyValuePair<string, int>[] table;
public HashTable(int capacity)
{
table = new KeyValuePair<string, int>[capacity];
}
public void Add(string key, int value)
{
int index = GetHash(key);
while (table[index].Key != null)
{
index = (index + 1) % capacity;
}
table[index] = new KeyValuePair<string, int>(key, value);
}
public int Get(string key)
{
int index = GetHash(key);
while (table[index].Key != null)
{
if (table[index].Key == key)
{
return table[index].Value;
}
index = (index + 1) % capacity;
}
throw new KeyNotFoundException();
}
}
上述代码示例中,我们使用一个数组来存储键值对。当发生冲突时,我们通过线性探测的方式,依次查找下一个可用的位置。
总结
散列表是一种常用的数据结构,它可以高效地存储和访问键值对。使用散列函数将键映射到数组索引,可以减少冲突的发生。当发生冲突时,我们可以使用链地址法或开放寻址法来解决。
在C#中,我们可以使用内置的Dictionary
类来实现散列表。如果需要自定义散列函数或解决冲突的方式,我们可以根据需求实现自己的散列表类。