【发布时间】:2011-07-04 18:25:41
【问题描述】:
查找操作 OR contains for single 在最坏的情况下可以是 O(n) 对吗?那么,对于n 元素,在hashSet 中查找将是O(n^2)?
【问题讨论】:
标签: java time-complexity
查找操作 OR contains for single 在最坏的情况下可以是 O(n) 对吗?那么,对于n 元素,在hashSet 中查找将是O(n^2)?
【问题讨论】:
标签: java time-complexity
是的,但这确实是最坏的情况:如果HashSet 中的所有元素都具有相同的哈希码(或通向同一个桶的哈希码)。使用正确编写的hashCode 和正态分布的密钥样本,查找时间为 O(1)。
【讨论】:
是的,但是我们拥有 HashSets 的全部原因是我们遇到这种最坏情况的概率非常非常低,而且它通常比保证的 nlogn 堆或(自平衡)TreeSet 或保证的n^2 表示未排序的列表。
【讨论】:
正如前面的答案中已经指出的,查找时间复杂度为 O(1)。为了确保它是真实的,只需查看contains() 的源代码:
...
private transient HashMap<E,Object> map;
...
public boolean contains(Object o) {
return map.containsKey(o);
}
...
如您所见,它在内部使用HashMap 对象来检查您的对象是否存在。
然后,如果我们看一下contains() for HashMap 的实现,我们会看到如下代码:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
getNode() 根据键哈希值和键值搜索节点。请注意,hash(key) 的时间复杂度为 O(1)。
最后,对于getNode():
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab;
Node<K,V> first, e;
int n;
K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
最重要的部分基本上是第一个内部if块:
...
if (first.hash == hash &&
((k = first.key) == key || (key != null && key.equals(k))))
return first;
...
如果你的对象 key 和第一个元素 first 的哈希值相等,并且对象本身也相等(显然!),那么 first 就是我们正在寻找的对象,这是 O (1).
正如你所看到的,这一切都取决于哈希函数的实现——如果它很好,那么它将主要为不同的关键对象分配不同的桶。如果不是,那么几个关键对象可能驻留在同一个存储桶中,因此我们需要在存储桶本身中进行查找以找到正确的键,如下所示:
...
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
...
} while ((e = e.next) != null);
但是,即使在这种情况下,如果您的存储桶是 TreeNode,它也是 O(log(k))(k - 存储桶中的元素数),因为它是平衡的二叉搜索树。如果不是(else 块),则为 O(k)。但同样,这种情况很少发生(对于某些类型的对象甚至可能永远不会发生),因此一次调用 contains 方法的平均时间复杂度将保持为 O(1)。显然,如果您执行n 调用,那么总时间复杂度将是线性的。
【讨论】:
lookp 需要 O(c)
c = 常数值
【讨论】: