在C#中创建循环链表的最佳方法是什么?我应该从LinkedList<中获取它吗? T>采集?我打算使用这个链接列表创建一个简单的地址簿来存储我的联系人(这将是一本糟糕的地址簿,但我不在乎因为我将是唯一一个使用它的人)。我主要想创建一个关键链表,以便我可以在其他项目中再次使用它。
如果您认为链接列表不是正确的方式,请告诉我哪种方式会更好。
答案 0 :(得分:78)
由于大多数答案实际上并未涉及问题的实质内容,仅仅是意图,或许这会有所帮助:
据我所知,链接列表和循环链接列表之间的唯一区别是迭代器在到达列表的结尾或开头时的行为。支持循环链表的行为的一种非常简单的方法是为LinkedListNode编写扩展方法,该方法返回列表中的下一个节点,如果不存在这样的节点,则返回第一个节点,类似地,用于检索前一个节点或最后一个节点。一个,如果没有这样的节点。以下代码应该完成,尽管我还没有测试过:
static class CircularLinkedList {
public static LinkedListNode<T> NextOrFirst<T>(this LinkedListNode<T> current)
{
return current.Next ?? current.List.First;
}
public static LinkedListNode<T> PreviousOrLast<T>(this LinkedListNode<T> current)
{
return current.Previous ?? current.List.Last;
}
}
现在你可以调用myNode.NextOrFirst()而不是myNode.Next,你将拥有循环链表的所有行为。您仍然可以执行常量时间删除,并在列表中的所有节点之前和之后插入等。如果我缺少循环链表的其他一些关键位,请告诉我。
答案 1 :(得分:6)
从BCL LinkedList类派生可能是个坏主意。该类被设计为非循环列表。试图让它循环只会给你带来麻烦。
你写自己可能要好得多。
答案 2 :(得分:4)
我认为循环链表不是联系人列表的正确数据结构。一个简单的列表&lt;&gt;或集合&lt;&gt;应该就够了。
答案 3 :(得分:4)
您是否有特定要求使用循环链接列表(即家庭作业)?如果没有,我建议使用简单的List<T>
类来存储您的联系人。
答案 4 :(得分:3)
循环链接列表通常使用数组实现,这使得它们非常快,并且本质上不需要动态调整大小。您只需要快速检查读取和写入索引,看看它们是否从最终落下,如果是,则将其重置为零(或者一个,无论如何)。
但是,它们通常用于输入缓冲区之类的东西,其中一旦读取数据就没有实际值。联系人列表具有持久价值,一旦列表填满,新联系人将覆盖旧联系人,这可能没问题,除非你覆盖了你遗嘱中留下一堆现金的祖母。
我不认为链表是最有效的循环缓冲区(原始问题)。
循环缓冲区的目的是速度,并且在循环缓冲区的上下文中,数组根本无法用于速度。即使您保留指向上次访问的链接列表项的指针,数组仍然会更有效。列表具有循环缓冲区不需要的动态调整大小功能(开销)。 话虽如此,我认为循环缓冲区可能不是您提到的应用程序(联系人列表)的正确结构。
答案 5 :(得分:3)
class CircularArray<T> : IEnumerator<T>
{
private readonly T[] array;
private int index = -1;
public T Current { get; private set; }
public CircularArray(T[] array)
{
Current = default(T);
this.array = array;
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
if (++index >= array.Length)
index = 0;
Current = array[index];
return true;
}
public void Reset()
{
index = -1;
}
public void Dispose() { }
}
答案 6 :(得分:2)
基于模数的解决方案。
如果将循环缓冲区实现为原始数组(或任何其他重要的集合)
T[] array;
我们将int current_index
存储到当前项的索引中,我们可以按如下方式在缓冲区中上下循环:
T NextOrFirst()
{
return array[(current_index + 1) % array.Length];
}
T PreviousOrLast()
{
return array[(current_index + array.Length - 1) % array.Length];
}
任何XAML绑定集合都可以使用相同的方法。
答案 7 :(得分:1)
答案 8 :(得分:0)
我认为这个问题最正确的数据结构是一个循环的双向链表。使用此数据结构,您可以在联系人列表中自由地上下移动
答案 9 :(得分:0)
class Program
{
static void Main(string[] args)
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7 };
IEnumerable<int> circularNumbers = numbers.AsCircular();
IEnumerable<int> firstFourNumbers = circularNumbers.Take(4); // 1 2 3 4
IEnumerable<int> nextSevenNumbersfromfourth = circularNumbers
.Skip(4).Take(7); // 4 5 6 7 1 2 3
}
}
public static class CircularEnumerable
{
public static IEnumerable<T> AsCircular<T>(this IEnumerable<T> source)
{
if (source == null)
yield break; // be a gentleman
IEnumerator<T> enumerator = source.GetEnumerator();
iterateAllAndBackToStart:
while (enumerator.MoveNext())
yield return enumerator.Current;
enumerator.Reset();
if(!enumerator.MoveNext())
yield break;
else
yield return enumerator.Current;
goto iterateAllAndBackToStart;
}
}
如果您想要更进一步,请制作一个CircularList
并按住相同的枚举器,以便在样本中旋转时跳过Skip()
。