HashSet查找复杂性?

时间:2011-07-04 18:25:41

标签: java time-complexity

在最坏的情况下,单个查找操作或contains可以是O(n)吗?因此,对于n元素,hashSet中的查找将是O(n^2)

4 个答案:

答案 0 :(得分:61)

是的,但这确实是最糟糕的情况:如果HashSet中的所有元素都具有相同的哈希码(或通向同一个桶的哈希码)。使用正确编写的hashCode和正态分布的密钥样本,查找为O(1)。

答案 1 :(得分:6)

是的,但我们拥有HashSets的全部原因是我们遇到这种最坏情况的概率非常非常低,并且它通常比保证nlogn对于堆或(自我平衡)TreeSet要快得多,或者保证n ^ 2表示未排序的列表。

答案 2 :(得分:0)

正如在前面的答案中已经指出的,查找时间复杂度为 O(1)。为了确保它是真的,只需查看 contains() 的源代码:

...

private transient HashMap<E,Object> map;

...

public boolean contains(Object o) {
    return map.containsKey(o);
}

...

如您所见,它在内部使用 HashMap 对象来检查您的对象是否存在。

然后,如果我们查看 contains()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 调用,则总时间复杂度将是线性的。

答案 3 :(得分:-17)

lookp需要O(c)

c =常数值