Java合并O(1)中的2个集合

时间:2011-12-03 13:54:05

标签: java algorithm collections merge

我需要能够将2个大型集合合并到1.我可以使用哪种集合类型?我不需要随机访问各个元素。通常我会选择一个链表,但是我不能将Java中的2个链表与O(1)的运行时合并,这可以在许多其他语言中完成,因为我必须将每个元素复制到新的列表中

编辑:谢谢你的所有答案。你的答案都非常有用,我设法完成了工作。下次我将使用自己的链接列表实现开始。

8 个答案:

答案 0 :(得分:57)

您可以使用GuavaIterables.concat方法之一在O(1)中创建连续的Iterable视图:

Iterable<T> combined = Iterables.concat(list1, list2);

这将允许您将两个列表的所有元素作为一个对象进行迭代而不复制任何元素。

答案 1 :(得分:12)

这里最简单的解决方案是列表清单。意味着您需要一些简单的包装函数,但并不复杂。

答案 2 :(得分:10)

理论上,您可以在O(1)中合并2个链接列表,因为您所要做的就是将第一个节点指向第二个节点(假设您有这些参考)。

addAll收集方法似乎意味着O(n)的运行时间,因为它们正在讨论迭代器。细节可能是特定于JVM的......

我认为没有任何集合会在O(n)中合并。你可能需要自己动手。

答案 3 :(得分:3)

我认为你最好的方法是创建一个List的实现,它采用List&gt;作为其论点,然后代表。换句话说,有一个列表列表,并将它们连接起来作为一个列表。当您浏览列表1的元素时,您将开始查看列表2。

出于某种原因,我认为番石榴有这样的清单。但我无法在他们的javadocs中找到它。

答案 4 :(得分:2)

如果只是希望拥有对象集合并在O(1)时间内合并它们,并且不介意实现自己的数据结构,那么最简单的方法是使用不平衡的二叉树:每个节点都是一个叶子(存储一个值)或两个树的组合,你可以用抽象的超类或接口将它们实现为两个类。深度优先遍历可用于提取元素。

这与ColinD关于迭代器连接的建议基本相同,但更多的是基础。

问题是迭代这个集合是O( n )!它将是O( n + m ),其中 m 是您执行的合并次数(因为每个是要遍历的节点) )。我的解决方案和ColinD都是如此。我不知道这个问题的所有可能解决办法是否属实。

别介意以上。在此方案下,每个合并至少添加一个元素,因此 m &lt; n ,因此迭代成本仍为O( n )。 (如果你使用迭代器连接,请确保你不经常连接空迭代器,因为这会增加成本。)

答案 5 :(得分:1)

合并链接列表确实是O(1),您可以采用相同的方式考虑基于数组的列表,即在其间链接多个Object []。

有上述实现,从中/从中删除/插入时比ArrayList更快。 迭代几乎是一样的。随机访问可能会稍微慢一些。

答案 6 :(得分:1)

我想从apache.commons推荐CompositeCollection类,但是看看source code这个也在O(n)中运行。 如果您只需迭代元素并且不想按照ColinD的建议使用Google Collections,您可以轻松创建自己的复合迭代器,例如。

public class CompositeCollectionIterator<T> implements Iterator<T>{

  private Iterator<T>[] iterators;
  private int currentIteratorIndex = 0;
  public CompositeCollectionIterator( Collection<T>... aCollections ) {
    iterators = new Iterator[ aCollections.length];
    for ( int i = 0, aCollectionsLength = aCollections.length; i < aCollectionsLength; i++ ) {
      Collection<T> collection = aCollections[ i ];
      iterators[i] = collection.iterator();
    }
  }

  public boolean hasNext() {
    if ( iterators[currentIteratorIndex].hasNext() ) return true;
    else if ( currentIteratorIndex < iterators.length - 1 ){
      currentIteratorIndex++;
      return hasNext();
    }
    return false;
  }

  public T next() {
    return iterators[currentIteratorIndex].next();
  }

  public void remove() {
    iterators[currentIteratorIndex].remove();
  }
}

答案 7 :(得分:0)

通过使用两个链接列表作为集合,并存储指向每个列表的第一个最后一个元素的指针(在添加/删除项目时可能需要更新这两个指针),您可以合并O(1)中的两个列表 - 只需将第一个列表的最后一个元素连接到第二个列表的第一个元素,并相应地调整第一个/最后一个指针。

我担心您需要在Java中滚动自己的链接列表实现,因为您无法直接访问LinkedList的基础节点,因此您无法连接最后一个第一个列表的元素到第二个列表的第一个元素。

幸运的是,很容易在Java中找到链表实现,因为它是数据结构课程中非常常见的主题。例如,here是一个 - 我知道,名称是西班牙语,但ListaEncadenada(“LinkedList”)和NodoLista(“ListNode”)中的代码非常简单,应该是不言自明的,最重要的是 - 实现包含指向列表的第一个和最后一个元素的指针,并且可以很容易地修改以满足您的需要。