【问题标题】:What is the time complexity of HashMap.containsValue() in java?java中HashMap.containsValue()的时间复杂度是多少?
【发布时间】: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,7}sum 8 匹配,它应该返回true

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

【问题讨论】:

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

标签: java performance optimization hashmap


【解决方案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 中值的数量,不是我们通常用来表示其复杂度的键/大小。

    【讨论】:

      猜你喜欢
      • 2012-02-13
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 2014-10-04
      • 1970-01-01
      • 2018-11-24
      • 2014-05-27
      相关资源
      最近更新 更多