【发布时间】:2011-10-27 16:56:41
【问题描述】:
我正在编写一个程序,它将对文档中的单词以及它们的使用频率和行号进行哈希处理。当我被告知必须从头开始创建哈希表时,我以为我已经完成了。我不知道从哪里开始。任何关于从哪里开始以及如何开始的建议都将不胜感激。
【问题讨论】:
-
这是家庭作业吗?
-
从数组和哈希函数开始。
我正在编写一个程序,它将对文档中的单词以及它们的使用频率和行号进行哈希处理。当我被告知必须从头开始创建哈希表时,我以为我已经完成了。我不知道从哪里开始。任何关于从哪里开始以及如何开始的建议都将不胜感激。
【问题讨论】:
哈希表是一种重要的基础数据结构。您可以在Wikipedia's Hash table article 阅读有关它的更多信息。幸运的是,它们很容易实现。
基本上,哈希表是一种数据结构,它接受一个键并返回或存储该键的值。
它们的核心通常是使用数组实现的,我们称之为arr,这样键值对存储在arr[key.hashCode()%arr.length]。请注意,由于您的数组不是无限长的,并且 hashCode 不能保证产生唯一值,因此您最终会得到映射到数组相同索引的键。这称为碰撞。
解决这些冲突的一种方法是为arr 的每个成员存储一个链表。那么arr 的定义应该是这样的
LinkedList<Object> arr[];
映射到arr[key.hashCode()%arr.length] 的所有对象都将添加到该位置的列表中。当您想从哈希表中检索对象时,跳转到位于arr[key.hashCode()%arr.length] 的链表并遍历链表的每个成员,直到找到键为.equals 的键值对。
一个好的哈希表实现可能会在它变得太满时执行诸如调整arr 大小之类的操作。
【讨论】:
要创建哈希表,您需要记住要构建的结构。哈希表有 2 个元素,一个键集和一个值集。考虑一下您将如何表示这些以及如何确保键始终映射到适当的值。
接下来,考虑一下您的哈希函数。请记住,没有“正确”的哈希函数。你只需要想出一个适合你的。
这两件事做好了,剩下的就很简单了。编写处理这两个集合的方法基本上只是巧妙地使用哈希函数的问题。
祝你好运!
额外提示:请记住,哈希表的值集不一定与键集大小相同。记住桶的概念。这很重要。
【讨论】:
只需为您的表格使用一个数组。您将需要一个哈希函数和一个负载因子。负载因子将决定您何时调整表格大小以保持高效。字符串的一个简单哈希函数是将每个字符的 ascii 值相加乘以 2^(字符索引),然后将该总和乘以表长度。例如
static int hash(String s) {
int len = 30;
int sum = 0;
for(int i = 0; i < s.length(); i++) {
sum += ((int)s.charAt(i)) * (1<<i);
}
return sum%len;
}
您也可以使用String.hashCode。要解决冲突,最简单的方法是线性探测,但会很快发生聚类。您可以实现双重哈希以获得更好的性能。
【讨论】:
从学习HashMap Java 类开始。请注意,它是AbstractMap 的扩展,AbstractMap 提供了许多您想要的基本功能。然而,并不是所有的。最重要的方法 put() 总是抛出 UnsupportedOperationException。这是设计使然。要使其成为 MyHashMap,您需要重写此方法。在 put() 内部,您将执行 Jack's great answer 中提到的所有杂乱无章的事情。
public class MyHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
// your implementation goes in here, including the override of put()
}
通过这种方式,您可以更轻松地测试您的实现。运行一系列测试,其中您提供一个 HashMap 作为参数。然后使用您的 MyHashMap 对象运行相同的代码。由于两者都是 Map 的实现,因此您可以在不更改任何代码的情况下替换另一个,并且您会对测试告诉您的内容充满信心。
【讨论】: