Java中的链表有快速的concat方法吗?

Posted

技术标签:

【中文标题】Java中的链表有快速的concat方法吗?【英文标题】:Is there a fast concat method for linked list in Java? 【发布时间】:2011-01-30 10:39:26 【问题描述】:

如何通过 jdk1.6、google 或 apache commons 集合或其他方式将 O(1) 中的两个链表与 Java 连接起来?例如。在 jdk 中只有 addAll 方法是 O(n)。

我想念的另一个功能是连接两个列表,其中每个列表都可以按相反的顺序排列。为了说明这一点,假设两个列表 a->b->c 和 e->f->g 可以合并到

    a->b->c->e->f->g a->b->c->g->f->e c->b->a->e->f->g c->b->a->g->f->e

您知道这样的列表实现还是我必须实现自己的链表?了解如何调整现有解决方案也很有帮助(例如 jdk LinkedList 只有很多私有方法)。这些功能在我看来非常明显,希望我没有遗漏一些愚蠢的东西。

正如 MicSim 指出的问题 Merge two lists in constant time in Java 是相关的,但不是真正的重复! 现在的问题是:

    是否可以使用其他集合库? 如何连接逆?

【问题讨论】:

可能重复。看到这个:***.com/questions/2237308/… 你能用语言中立的方式描述一个算法,它在 O(1) 中连接链表的内容吗? @rocker:欢迎来到 SO。为了将来参考,仅仅因为 SO 的结构方式,最好将甚至相关的主题(在你的情况下,concat 和 inverse)作为单独的问题询问。人们希望能够一次专注于一个主题。 @David:独立于语言,但类似于 java 的算法:firstList.getLastElement().setNextElement(secondList.getFirstElement()) @Roman 这是 O(N),其中 N 是 secondList 的大小。 【参考方案1】:

目前我看到的唯一解决方案是实现List,制作一个像这样的构造函数:

public EnhancedList (List l1, List l2)

并覆盖所有方法。在这样的解决方案中,是否要连接 LinkedLists 或任何其他列表实际上并不重要。

【讨论】:

我实际上虽然会有一个开箱即用的解决方案。如果我必须推出我自己的,我会通过 MyLinkedList.addAll(Collection) -> 检测 LinkedLists 并创建逆: MyLinkedList.inverse() 我只是提出了相同的解决方案(所以 +1 ;))。我认为 Roman 提议的是实现一个 List 接口,以便您可以将此实现用作单个列表,而它的方法适用于创建它的单个列表。例如,新的get 方法将从第一个列表或第二个列表返回一个元素:public T get(int i) return i < list1.size() ? list1.get(i) ? list2.get(i - list1.size()); 【参考方案2】:

我认为使用任何类型的基本列表构造编写这都不会太难,因为在链接列表中插入列表的中间或末尾是 O(1)。

【讨论】:

问题是 LinkedList 在 Java 中是一个独立的对象,并且执行该操作 同时还保持 LinkedList 对象的完整和独立不是 O(1) 操作,因为您必须复制一个列表。 好点,我未能深入挖掘 Java 特定对象。【参考方案3】:

如果你愿意接受 Iterable 结果,你可以使用 google-collections Iterables.concat 和 Iterables.reverse

http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Iterables.html

public static <T> Iterable<T> concat(Iterable<? extends T> a,
                                 Iterable<? extends T> b)

public static <T> Iterable<T> concat(Iterable<? extends T> a,
                                 Iterable<? extends T> b,
                                 Iterable<? extends T> c)

public static <T> Iterable<T> concat(Iterable<? extends T> a,
                                 Iterable<? extends T> b,
                                 Iterable<? extends T> c,
                                 Iterable<? extends T> d)

public static <T> Iterable<T> concat(Iterable<? extends T>... inputs)

public static <T> Iterable<T> concat(Iterable<? extends Iterable<? extends T>> inputs)

【讨论】:

我会说看看代码 - 我怀疑它的性能不如 addAll 。 这些方法创建了一个委托给原始列表的新对象。检查性能/内存影响总是好的,因为这取决于使用情况。 使用 Iterables 方法,您不会复制列表元素,从而减少内存使用量,并且与 addAll 相比可能会提高速度。 使用 ArrayList 我注意到在经常访问迭代器时简单地使用 addAll 并生成一个新列表要快得多。这在我们的代码中经常发生,使用 addAll 时我们的性能提高了 50%。 ;-)【参考方案4】:

在以下情况下,调整 LinkedList 以在 jdk 中获得 O(1) concat 将起作用:

    方法 entry() 和变量 header 和 size 以及内部类 Entry 将受到保护

    addAll 会检测 LinkedList 然后做某事。喜欢:

    JdkLinkedList secondList = (JdkLinkedList) secondColl;
    int numNew = secondList.size();
    if (numNew == 0)  return false;
    modCount++;
    Entry<E> successor = (index == size ? header : entry(index));
    Entry<E> predecessor = successor.previous;
    // TODO LATER if reverse
    
    // connect the last element of this list with the first element of the second list
    // linked list is implemented as a 'cycle' => header.next == first element of second list
    Entry<E> first = secondList.header.next;
    predecessor.next = first;
    first.previous = predecessor;
    
    // append the last element of the second list with the rest of this list
    Entry<E> last = secondList.header;
    successor.previous = last;
    last.next = successor;
    

【讨论】:

【参考方案5】:

对于 concat,我建议您执行以下操作:

确保所有参数/变量都声明为 List<...> 而不是 LinkedList<...> 改变新的链表<...>();到新的 ArrayList<...>(); 配置应用程序 将新的 ArrayList<...> 更改为新的 LinkedList<...>(); 配置应用程序

根据您的使用情况,ArrayList 可能比 LinkedList 快得多。还可以查看配置文件数据,您可以看到使用 addAll 对性能有多大影响 - 如果它不是那么大,请不要费心“修复”它。

对于某些事情,您使用其他语言的经验并不适用。您可能会发现 addAll 符合您在 Java 中的要求。

如果您要编写自己的可连接列表,请确保它符合 List 接口,然后更改您的代码并重新配置它并确保它更快。如果不是,则将其丢弃并坚持使用标准 List 类型。

【讨论】:

我确信在我的用例中,链表的 concat 会比 arraylist 的 system.copy 更快 更多...例如,如果您对列表进行大量迭代,则对链表进行迭代的成本将超过对数组列表的迭代成本。因此,在 addAll 中花费的时间可能会抵消发送迭代的时间。还有其他类似的东西可以使 ArrayList 的整体性能比 LinkedList 更快。但实际上,永远不要假设性能 - 总是衡量。 当然我会测试差异!但问题是关于一种快速的 concat 方法,而不是关于整体性能。 但是一个快速的 concat 可能在性能方面毫无意义,这可能解释了为什么一个 doers 不存在。【参考方案6】:

FastList from javolution 不是开箱即用的解决方案,但 tail() 和 head() 非常接近我的最爱。

我认为trove 是解决方案,尽管在 O(1) 中不存在用于 invert 或 addAll 的便捷方法。

【讨论】:

以上是关于Java中的链表有快速的concat方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

C语言当中的链表重要吗?可以做啥?

使用类的链表中的分段错误。我的方法正确吗?

数据结构中的链表的操作使用java语言的实现

常见的链表排序(Java版)

递归地反转Java中的链表

leetcode-合并两个排序的链表-66