【问题标题】:Time complexity of copying a Collection of strings to another in Java在 Java 中将字符串集合复制到另一个字符串的时间复杂度
【发布时间】:2020-06-01 10:03:11
【问题描述】:

我有几个关于 Java Collection 的 add 函数如何处理字符串的问题。例如,在下面的代码 sn-p 中,我将字符串的 List 复制到 HashSet。在这种情况下,最坏情况下的总时间复杂度是多少?是 O(M x N) 还是 O(N),其中 M 是列表中任意字符串的最大长度,N 是列表中字符串的总数。

public HashSet<String> createDict(List<String> wordList) {
   HashSet<String> wordDict = new HashSet<>();
   for(String word : wordList) {
       wordDict.add(word);
   }
   return wordDict;
}

如果我使用下面的代码而不是循环,时间复杂度会完全相同吗?

HashSet<String> wordDict = new HashSet<>(wordList);

【问题讨论】:

  • "是 O(M x N) 还是 O(N),其中 M 是列表中任何字符串的最大长度 [...]",可以你解释一下?为什么长度会影响复制过程?为什么它会与 N 相乘?
  • 列表的长度(N)会影响复制过程,因为我正在运行一个循环来遍历列表中的所有字符串。但我的问题是,特定字符串的最大长度(M)是否也会影响副本的时间复杂度?如果是,为什么?
  • 它没有,这与HashSet 如何添加元素和工作有关。请参阅以下提供相关信息的答案。 但是这是现实的答案。您可能会争辩说长度 M 必须至少被读取一次。读取是逐个字符完成的,如果你对每个 n 都进行读取,那么复杂度确实是 O(M x N)。但这是“我非常重视 O 符号”的答案。
  • 没有字符串按值复制,仅按引用复制,因此它们的长度无关紧要。

标签: java string collections time-complexity


【解决方案1】:

字符串的长度与在集合之间复制元素无关。实际上,您不会复制字符串本身,而是复制对它们的引用。所以复杂度将是 O(N)。

当谈到关于new HashSet&lt;&gt;(wordList) 的第二个问题时——这个调用将比循环更快。原因是在HashSet(Collection) 构造函数中,它首先检查该集合的大小,并基于此从initialCapacity 开始。这样它就不必经常调整底层 HashMap 的大小。

对于那些好奇而懒得搜索的人,这是HashSets 有问题的构造函数:

public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

还有来自AbstractCollectionaddAll

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

因此,如果您要在示例代码中设置 initialCapacity,您将获得相同的性能,如下所示:

public HashSet<String> createDict(List<String> wordList) {
   int initialCapacity = Math.max((int) (wordList.size()/.75f) + 1, 16);
   HashSet<String> wordDict = new HashSet<>(initialCapacity );
   for(String word : wordList) {
       wordDict.add(word);
   }
   return wordDict;
}

【讨论】:

  • 感谢您的回答。如果我将字符串从一个列表复制到另一个列表会发生什么?也会是 O(N) 吗?
  • 完全一样。
  • 初始容量是否有影响,取决于原始列表有多少副本。
【解决方案2】:

复杂度为 O(N)。

将一个项目添加到HashSet 是 O(1),它不会逐个字符地比较字符串,这可能是你得到 O(MxN) 的方式。

是的,创建HashSet 在构造函数中传递列表将具有相同的复杂性。 实际上,您可以检查HashSet 实现代码,它与您所做的完全相同,除了根据您的列表大小更优化的对象创建。

【讨论】:

    【解决方案3】:

    HashSet 是使用HashTable 实现的。这意味着,它有O(1) 插入并且使用散列函数来插入一个元素。在这种情况下,插入元素的大小并不重要,它们都被认为是O(1)。因此,总体而言,代码具有O(N) 复杂性,其中N 是列表的大小。

    【讨论】:

      猜你喜欢
      • 2020-06-09
      • 1970-01-01
      • 2020-07-31
      • 2013-04-13
      • 2013-04-14
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 2022-11-30
      相关资源
      最近更新 更多