【问题标题】:Complexity of Set operations集合操作的复杂性
【发布时间】:2010-10-24 22:01:43
【问题描述】:

这就是我正在做的:
字符串一 = “一些字符串”
字符串二 = “一些字符串”

我想知道字符串 one 和 two 中的所有字符,它们应该按照字符串 1 中的顺序排列

我编写了一个 Java 程序,它通过使用 Collections 对两个集合执行集合操作。
我想知道的是,执行集合运算的复杂度是多少,是多项式时间还是线性时间

我的程序在这里

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package careercup.google;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author learner
 */
public class CharaterStringsIntersection {
    private static final String one = "abcdefgabcfmnx";
    private static final String two = "xbcg";

    public static void main(String args[]){
        List<Character> l_one = new ArrayList<Character>();
        List<Character> l_two = new ArrayList<Character>();

        for(int i=0; i<one.length(); i++){
           l_one.add(one.charAt(i));
        }
        for(int j=0; j<two.length(); j++){
            l_two.add(two.charAt(j));
        }

        l_one.retainAll(l_two);

        Iterator iter = l_one.iterator();
        while(iter.hasNext()){
            System.out.println(" > " + iter.next());
        }
    }
}

输出:

run:
 > b
 > c
 > g
 > b
 > c
 > x

【问题讨论】:

  • 你可以使用LinkedHashSet来获得一个Set的性能,同时保持顺序。注意:你只需要在第一个集合中保留顺序,第二个的顺序不重要??您需要保留重复项吗?如果是这样,第一个集合必须是一个列表,但第二个集合仍然可以是一个集合。

标签: java set complexity-theory


【解决方案1】:

其复杂度为 O(N * (N + M)),其中 N = one.length(),M = two.length()。

它的工作方式如下:对于l_one的每个字符(有N个)它扫描l_two(因为l_two是ArrayList,扫描是线性的,所以它需要O(M ) 步骤,请参阅 [1]) 并在必要时从 l_one 中删除项目(从 ArrayList 删除需要 O(N),请参阅 [1]),因此您得到 O(N * (N + M))。

您可以通过使用LinkedList for l_one 将复杂度降低到O(N * M)(因为从LinkedList 中删除是O(1),请参阅[2]),并进一步 使用 TreeSet 将其降低到 O(N * log(M)) 以获取 l_two(因为在 TreeSet 中搜索需要 O(log(M)),请参阅 [3])。

参考资料:

  1. 所有其他操作都以线性时间运行 (ArrayList javadoc)
  2. 所有操作都按照双向链表的预期执行,(LinkedList javadoc)
  3. 此实现为基本操作(add、remove 和 contains)提供有保证的 log(n) 时间成本 (TreeSet javadoc)

【讨论】:

  • 是否保证/指定了 Java 收集方法的复杂性(以 STL 提供保证的方式)?我在 Javadocs 中找不到任何内容...
  • @Oli:我添加了参考资料。
  • 你在 TreeSet 中有一个错字需要 O(M) - 它应该是 O(log(M)),因为你在总复杂度中正确使用了它
【解决方案2】:

我认为您要问的更重要的问题是 retainAll() 函数的复杂性是什么。假设没有散列或快速查找,我会假设时间复杂度为 O(n^2)。一个循环迭代列表,一个循环比较每个项目。

【讨论】:

  • 有人告诉我这是一个面试问题;)我认为您应该开始考虑其他数据结构。如果你使用哈希,这个问题可能是 O(n)
【解决方案3】:

你使用了错误的集合来达到你的目的..

由于您使用普通的ArrayList 进行操作,因此复杂性不会很好,我不知道内部实现,但我猜它是二次的(因为它必须滚动两个列表),除非 Java 内部转换在进行交集之前要设置两个列表。

你应该从Set接口(here)开始,选择哪个集合更适合你的需求,我认为HashSet(常数时间)或TreeSet(对数但能保持自然订购)将是最好的解决方案。

【讨论】:

    【解决方案4】:

    简单地说:

    • ArrayList.add() 是O(1)。

    • ArrayList.retainAll(other) 是 O(N1 * N2),其中 N1 是 this 的大小,N2 是 other 的大小。 (实际成本取决于保留元素与移除元素的比例。)

    • iter.hasNext() 和 iter.next() 是 O(1) 的 ArrayList 迭代器。

    这一切都如您所料,基于将 ArrayList 心理建模为 Java 数组。

    【讨论】:

      猜你喜欢
      • 2011-11-13
      • 1970-01-01
      • 2011-09-24
      • 1970-01-01
      • 1970-01-01
      • 2014-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多