2009-04-04 21 views
21

C#で循環リンクリストを作成する最良の方法は何でしょうか。私はLinkedList < T>コレクションからそれを派生させるべきですか?私は、このリンクされたリストを使用して連絡先を保存するためのシンプルなアドレス帳を作成しようと考えています(それは吸うようなアドレス帳になるでしょうが、私はそれを使う唯一の人ではないので気にしません)。私は主に、他のプロジェクトで再び使用できるように、重要なリンクのリストを作成したいと思っています。C#で循環リンクリストを作成していますか?

あなたがリンクされたリストが正しい方法であると思わないなら、どちらの方が良いかを教えてください。

答えて

74

、多分これは役立ちます:

を私の知る限りは、リンクされたリストと円形の唯一の違いを言うことができるようにLinked Listは、リストの最後または最初に到達したときのイテレータの動作です。 Circular Linked Listの動作をサポートする簡単な方法は、リスト内の次のノードを返すLinkedListNode、またはそのノードが存在しない場合は最初のノードを返す拡張メソッドを記述することです。同様に、前のノードまたは最後のノードそのようなノードが存在しない場合は1です。

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の):私はそれをテストしていないが、次のコードは、それを達成する必要があります。次に、循環リンクリストのすべての動作を行います。一定の時間の削除を行い、リスト内のすべてのノードの前後に挿入することができます。私が行方不明の循環リンクリストの他の重要なビットがある場合は、私に知らせてください。

+8

それはちょうど完璧な男です、ありがとう!スタイルを改善するために、 '??'演算子:return current.Next ?? current.List.First; – Julien

+1

@Julien言語の複雑さは必ずしも読みやすくするものではありません。 –

+2

ここで重要な点の1つは、ノード自体がリンクされていない場合、 'current.List'が潜在的にnullになる可能性があることです。 https://msdn.microsoft.com/en-us/library/h339c45b(v=vs.110).aspxを参照してください。 –

4

循環リンクリストは、連絡先リストの正しいデータ構造ではないと思います。単純なList <>またはCollection <>で十分です。

6

BCL LinkedListクラスから派生するのは悪い考えです。そのクラスは、非循環のリストになるように設計されています。円形にしようとすると、問題が発生するだけです。

あなたはおそらくあなた自身を書く方がはるかに良いでしょう。

4

循環リンクリスト(宿題)を使用するための具体的な要件はありますか?そうでない場合は、単純なList<T>クラスを使用して連絡先を保存することをお勧めします。

3

サークルリンクリストは、非常に高速になり、その性質上動的なサイズ変更を必要としない配列を使用して実装されることがよくあります。読み込みインデックスと書き込みインデックスを素早くチェックして、それらが終わっていないかどうかを確認するだけで済みます。もしそうなら、ゼロにリセットしてください。

しかし、一般的には、入力バッファなどのようなものに使用されます。入力バッファでは、一度読み取ったデータには実際の値はありません。連絡先リストは永続的な価値があり、新しい連絡先はリストがいっぱいになると古い連絡先を上書きします。


リンクされたリストは循環バッファ(元の質問)に行くのに最も効率的な方法だとは思いません。

循環バッファの目的はスピードであり、循環バッファのコンテキストでは速度のために配列を単純に叩くことはできません。最後にアクセスしたリンクリスト項目へのポインタを保持しても、配列はより効率的です。リストには循環バッファに不要な動的リサイズ機能(オーバーヘッド)があります。 これは、循環バッファーはおそらくあなたが言及するアプリケーション(連絡先リスト)の正しい構造ではないと思います。これらの答えのほとんどは、実際に質問、単に意思の物質では得られないとして

+0

'非常に高速になり、その性質上、動的なサイズ変更を必要としない配列 - なぜそれを言うのですか? – nawfal

+0

彼はおそらく、循環リストを使用している場合は、設定された容量があり、それを超えて動的にサイズを変更する必要がないからです。 – bgura

0

この問題の最も正しいデータ構造は、循環的な二重リンクリストだと思います。このデータ構造では、あなたがすることができ、自由に上下の連絡先リストを

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() { } 
} 
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()をスキップするために、同じ列挙子を保持し、さらに行きたい場合。

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バインディングコレクションで使用できます。

関連する問題