23

有人会认为简单的代码

llist1.Last.Next = llist2.First;
llist2.First.Previous = llist1.Last;

会起作用,但显然在 C# 的 LinkedList 中,First、Last 和它们的属性是 Get only。

我能想到的另一种方法是

llist1.AddLast(llist2.First);

然而,这也不起作用——它失败了,因为 llist2 的第一个节点已经在一个链表中。

这是否意味着我必须有一个循环手动将 llist2 的每个节点 AddLast 到 llist1?这不会破坏链表的效率吗????

4

3 回答 3

18

是的,不幸的是,你必须循环。这是一个 O(n) 操作 - 添加的每个条目都是 O(1)。没有需要调整缓冲区大小和复制等的风险 - 尽管垃圾收集当然可能会大致做到这一点:) 你甚至可以编写方便的扩展方法:

public static class LinkedListExtensions   
{
    public static void AppendRange<T>(this LinkedList<T> source,
                                      IEnumerable<T> items)
    {
        foreach (T item in items)
        {
            source.AddLast(item);
        }
    }

    public static void PrependRange<T>(this LinkedList<T> source,
                                       IEnumerable<T> items)
    {
        LinkedListNode<T> first = source.First;
        // If the list is empty, we can just append everything.
        if (first is null)
        {
            AppendRange(source, items);
            return;
        }

        // Otherwise, add each item in turn just before the original first item
        foreach (T item in items)
        {
            source.AddBefore(first, item);
        }
    }
}

编辑:埃里希的评论暗示了为什么你可能认为这是低效的——为什么不通过更新第一个列表尾部的“下一个”指针和第二个列表头部的“上一个”指针来将两个列表连接在一起?好吧,想想第二个列表会发生什么……也会改变。

不仅如此,这些节点的所有权会发生什么变化?现在,每个基本上都是两个列表的一部分……但该LinkedListNode<T>.List物业只能谈论其中一个。

虽然我可以理解为什么在某些情况下您可能想要这样做,但 .NETLinkedList<T>类型的构建方式基本上禁止它。我认为这个文档评论解释得最好:

该类LinkedList<T>)不支持可能使列表处于不一致状态的链接、拆分、循环或其他功能。

于 2009-07-07T19:57:04.207 回答
8
llist1 = new LinkedList<T>(llist1.Concat(llist2));

这将连接两个列表(需要 .NET 3.5)。缺点是它创建了一个新的 LinkedList 实例,这可能不是您想要的……您可以这样做:

foreach(var item in llist2)
{
    llist1.AddLast(item);
}
于 2009-07-07T19:52:42.750 回答
5

在这里,您可以找到我的链表实现,其中包含 O(1) 连接和拆分时间。

为什么 .NET LinkedList 不支持 Concat 和 Split 操作?

简短的摘要

与 .NET LinkedList 相比的优势:

  • 更少的内存消耗,因此每个节点 SimpleLinkedListNode 具有三个指针(prev、next、value)而不是与原始 .NET 实现不同的四个(prev、next、list、value)。

  • 支持 O(1) 中的 Concat 和 Split 操作

  • 支持 O(1) 中的 IEnumarable Reverse() 枚举器——顺便说一下,我看不出它没有在 .NET LinkedList 上原生提供的任何原因。适当的扩展方法需要 O(n)。

缺点:

  • 不支持计数。
  • Concat 操作使第二个列表处于不一致的状态。
  • 拆分操作使原始列表处于不一致状态。
  • 您可以在列表之间共享节点。

其他:

  • 我选择了一种替代策略来实现枚举和查找操作,而不是更冗长和纯粹可读的原始实现。我希望负面的性能影响仍然微不足道。
于 2011-07-18T13:47:44.797 回答