java中HashMap.containsValue()的时间复杂度是多少?

Posted

技术标签:

【中文标题】java中HashMap.containsValue()的时间复杂度是多少?【英文标题】:What is the time complexity of HashMap.containsValue() in java? 【发布时间】:2013-05-21 09:02:19 【问题描述】:

O(n) 时间复杂度中给了我一个要解决的问题:

“给定一个数字列表和数字 x。查找列表中是否有 2 个数字加起来为 x?”

这是我的解决方案:

public class SumMatchResult 

  public static void main(String[] args)
    int[] numberList = 6,1,8,7,4,6;
    int requiredSum = 8;
    boolean isSumPresent = checkSumPresentHash(numberList,requiredSum);
    if(isSumPresent) 
      System.out.println("Numbers exist");
    else 
      System.out.println("Numbers donot exist");
    
  

  private static boolean checkSumPresentHash(int[] numberList, int requiredSum) 
    Map<Integer, Integer> m = new HashMap<Integer,Integer>();
    int count = 0;
    for(int i=0;i<numberList.length;i++)
      m.put(i, numberList[i]);
    
    for(int i=0;i<numberList.length;i++)
      if(m.containsValue(requiredSum - numberList[i]))
        count++;
      
    
    if(count>1)
        return true;
    
    return false;
  


我使用HashMap.containsValue() 而不是使用HashSet.contains(),它肯定具有O(1) 的复杂性,因为我必须考虑我的输入可能包含相同值的情况。例如,在上述情况下,我可以有一组输入值3,6,4,4,7sum 8 匹配,它应该返回true

我上述解决方案的时间复杂度取决于HashMap.containsValue() 方法的复杂度。请阐明containsValue() 方法的时间复杂度,并建议我在时间复杂度方面是否有上述问题的更好解决方案。谢谢。

【问题讨论】:

也许你应该使用Set 而不是Map 我很想知道有一种算法可以在 O(n) 时间内解决这个问题。我不确定这是否可能。 保持一个单独的HashMap 与第一个值作为他的键怎么样?这样您就可以立即知道 (O(1)) 是否存在值。这是O(2n) 空间,但嘿,这很值得。 @JBNizet 我上次看到它已经有很长时间了,但这闻起来像是背包问题的(非常)简化版本。动态编程在这里应该有所帮助。 @Bohemian 不,它是关于containsValue(),这是复杂性的根本区别。 【参考方案1】:

要回答标题中的问题 - 正如其他人所提到的,containsValue 是 O(n),因为没有密钥它不知道它在哪里,并且算法必须遍历存储在地图。

要回答问题正文中的问题 - 关于如何解决问题 - 只需考虑您是否真的需要一张通用地图,该地图可以计算您看到每个数字的实例数。我的意思是,唯一你会关心一个数字是否不止一次出现的时候是它的 x/2,对吧?这对我来说就像一个角落里的箱子。只需添加一个检查该极端情况的测试——比如在你的集合构造循环中嵌入if (numberList[i] == requiredSum/2) half++,然后在它之后嵌入if (requiredSum % 2 == 0 &amp;&amp; half == 2) return true(参见下面的其他变体)。

然后你可以遍历集合并为每个项目检查 requiredSum-item 是否也出现在集合中。

总结(尽可能提前退出):

Set<Integer> seen = new HashSet<Integer>();
boolean halfSeen = false;
for (int num : numberList) 
    if (num == requiredSum/2 && requiredSum % 2 == 0) 
        if (halfSeen) return true;
        halfSeen = true;
     else 
        seen.add(num);
    

for (int num : seen) 
    if (seen.contains(requiredSum - num)) return true;

return false;

【讨论】:

+1 关于角盒的好点(if (numberList[i] == requiredSum/2))。很棒的整体解决方案! @Oak :非常感谢您的解决方案。不过稍微修正一下,seen.add(num) 应该在 else 部分,否则,即使只有一个 num ==requiredSum/2,它也会被添加到集合中,并且下一个循环返回 true,这是错误的。 @VishnuVedula 好点,已修复。此更改还需要将除以二的检查移动到封闭的if,因为如果requiredSum 是7,那么我们确实希望在seen 中保留3。【参考方案2】:

HashMap 本质上是一个键值存储,它可以访问它的键,复杂度为 O(1)。然而,检查一个值,HashMap 只能检查所有值并查看它们是否等于您正在搜索的值。因此,复杂度为 O(n),其中 n 为 HashMap 中的元素个数。

另外一点:您在其装箱类型 (Integer) 的集合中查找原始值 (int)。这意味着每次您在 HashMap 上调用方法时,Java 都需要将原始值装箱:http://docs.oracle.com/javase/1.5.0/docs/guide/language/autoboxing.html

【讨论】:

以后一定会记住装箱和拆箱的。谢谢你:)【参考方案3】:

HashMap.containsValue 复杂度为 O(n)。但是 n 不完全是映射大小,而是哈希表大小,因为即使映射大小 = 0, containsValue 也会遍历所有表元素。 假设我们创建了一个初始容量 = 1024 的空映射。 containsValue 必须遍历 1024 个元素的哈希表数组:

public boolean containsValue(Object value) 
    if (value == null)
        return containsNullValue();

    Entry[] tab = table;
    for (int i = 0; i < tab.length ; i++)
        for (Entry e = tab[i] ; e != null ; e = e.next)
            if (value.equals(e.value))
                return true;
    return false;

【讨论】:

【参考方案4】:

HashMap 中没有任何值标记的索引,因此,要使用containsValue() 查找值,您必须遍历HashMap 的所有值。

请注意,这里的复杂度O(n) 是 HashMap 中值的数量,不是我们通常用来表示其复杂度的键/大小。

【讨论】:

以上是关于java中HashMap.containsValue()的时间复杂度是多少?的主要内容,如果未能解决你的问题,请参考以下文章

java中native的用法

java中Color的用法!

java中重载有啥用

java中int 和 Integer 有啥区别

java 中getBounds啥作用

java中怎么取出数组中的数组