【问题标题】:HashSet look-up complexity?HashSet 查找复杂度?
【发布时间】:2011-07-04 18:25:41
【问题描述】:

查找操作 OR contains for single 在最坏的情况下可以是 O(n) 对吗?那么,对于n 元素,在hashSet 中查找将是O(n^2)

【问题讨论】:

    标签: java time-complexity


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        正如前面的答案中已经指出的,查找时间复杂度为 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 调用,那么总时间复杂度将是线性的。

        【讨论】:

          【解决方案4】:

          lookp 需要 O(c)

          c = 常数值

          【讨论】:

            猜你喜欢
            • 2021-03-17
            • 1970-01-01
            • 2012-04-06
            • 1970-01-01
            • 2021-04-08
            • 1970-01-01
            • 1970-01-01
            • 2017-12-18
            • 2012-11-27
            相关资源
            最近更新 更多