web-dev-qa-db-ja.com

C#で循環リンクリストを作成しますか?

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

リンクリストが正しい方法だと思わない場合は、どちらの方法が良いか教えてください。

25
Kredns

これらの回答のほとんどは実際には質問の内容に到達しておらず、単に意図に基づいているため、おそらくこれが役立つでしょう。

私が知る限り、リンクリストと循環リンクリストの唯一の違いは、リストの最後または最初に到達したときのイテレータの動作です。循環リンクリストの動作をサポートする非常に簡単な方法は、リスト内の次のノードまたはそのようなノードが存在しない場合は最初のノードを返す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.Nextの代わりにmyNode.NextOrFirst()を呼び出すだけで、循環リンクリストのすべての動作が可能になります。リスト内のすべてのノードなどの前後に、一定時間の削除と挿入を行うことができます。私が見逃している循環リンクリストの他の重要な部分がある場合は、私に知らせてください。

80
Clueless

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

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

6
JaredPar

循環リンクリストが連絡先リストの正しいデータ構造であるとは思いません。単純なリスト<>またはコレクション<>で十分です。

4
Mitch Wheat

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

4
Samuel

循環リンクリストは、配列を使用して実装されることが多く、そのため非常に高速であり、その性質上、動的なサイズ変更は必要ありません。読み取りインデックスと書き込みインデックスを簡単にチェックして、それらが最後から外れていないかどうかを確認し、落ちている場合は、ゼロ(または1など)にリセットするだけです。

ただし、これらは通常、入力バッファなど、一度読み取られるとデータに実際の値がない場合に使用されます。連絡先リストには永続的な価値があり、リストがいっぱいになると新しい連絡先が古い連絡先を上書きします。これは、あなたにたくさんの現金を残している祖母を上書きしない限り、問題ない可能性があります。


リンクリストが循環バッファ(元の質問)を取得するための最も効率的な方法だとは思いません。

循環バッファの目的は速度であり、循環バッファのコンテキストでは、配列を速度で打ち負かすことはできません。最後にアクセスしたリンクリストアイテムへのポインタを保持している場合でも、配列の方が効率的です。リストには、循環バッファーには不要な動的なサイズ変更機能(オーバーヘッド)があります。そうは言っても、循環バッファは、あなたが言及したアプリケーション(連絡先リスト)にとっておそらく適切な構造ではないと思います。

3
RAL
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() { }
}
3

モジュラスベースのソリューション。

循環バッファが生の配列(またはそれが重要なもののための他の種類のコレクション)として実装されている場合

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

2
voccoeisuoi

これはどうですか CListベースの循環リスト

1
John Ellinwood

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

0
omar
_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()をスキップします。

0
Davi Fiamenghi