リンクされたリストはどのような状況で役立ちますか?


110

ほとんどの場合、リンクされたリストを使用しようとする人がいますが、私には選択が悪い(または非常に悪い)ように思えます。おそらく、リンクされたリストがデータ構造の適切な選択である、またはそうでない状況を調査することは有用でしょう。

理想的には、データ構造の選択に使用する基準、および特定の状況でどのデータ構造が最適に機能する可能性が高いかについて回答が説明されます。

編集:私は言わなければなりません、私は数だけでなく答えの質にもかなり感銘を受けました。私は1つしか受け入れることができませんが、もう少し良いものがなかったら、受け入れる価値があったと言えるでしょう。リンクされたリストが真の利点を提供する状況を指摘したカップル(特に、私が最終的に受け入れたもの)のみ。スティーブジェソップは、1つだけでなく3つの異なる答えを思いついたので、何らかの名誉ある言及に値すると思います。もちろん、回答ではなくコメントとしてのみ投稿されていたとしても、Neilのブログエントリも一読の価値があると思います。


34
2番目のパラグラフへの回答は1学期ほどかかります。
Seva Alekseyev、2010年

2
私の意見については、punchlet.wordpress.com / 2009/12/27 / letter-the-fourthを参照してください。これは調査のようですので、おそらくCWである必要があります。

1
@ニール、いいね、CSルイスが承認するかどうか疑わしいけど。
Tom

@ニール:私は一種の調査を推測します。主にそれは、誰かが少なくとも合理的に購入できる根拠のある答えを誰かが思い付くかどうかを確認する試みです。@セヴァ:はい、読み直しました。最後の文を当初の意図よりも少し一般的にしました。
Jerry Coffin

2
@Yar People(私を含め、申し訳ありませんが)は、ツリーと同様に、FORTRAN IV(ポインタの概念がなかった)などの言語で、ポインタなしのリンクリストを実装していました。「実際の」メモリの代わりに配列を使用しました。

回答:


40

これらは、並行データ構造に役立ちます。(現在、以下の非並行の実際の使用法のサンプルがあります- @NeilがFORTRANについて言及していなかった場合、サンプルはありません。;-)

たとえばConcurrentDictionary<TKey, TValue>、.NET 4.0 RCでは、リンクリストを使用して、同じバケットにハッシュするアイテムをチェーンします。

の基礎となるデータ構造ConcurrentStack<T>もリンクリストです。

ConcurrentStack<T>新しいスレッドプールの基盤として機能するデータ構造の1つです(ローカルの「キュー」は基本的にスタックとして実装されます)。(その他の主要な支持構造はConcurrentQueue<T>です。)

新しいスレッドプールは、新しいTask Parallel Libraryの作業スケジューリングの基礎を提供します 。

そのため、それらは確かに有用です。リンクリストは、現在、少なくとも1つの優れた新技術の主要なサポート構造の1つとして機能しています。

(単一リンクのリストでは、主な操作を単一のCAS(+再試行)で実行できるため、これらの場合、強制的なロックフリー -待機なしではない-を選択できます。最新のGC-d環境では、 Javaと.NET- ABAの問題は簡単に回避できます。新しく作成したノードに追加したアイテムをラップするだけで、それらのノードを再利用しないでください。GCに処理を任せてください。ABAの問題に関するページでは、ロックの実装も提供しています-無料のスタック-実際には.Net(&Java)で動作し、(GCされた)ノードがアイテムを保持します。)

編集:@ニール:実際、FORTRANについてあなたが言及したことは、同じ種類のリンクされたリストがおそらく.NETで最もよく使用され乱用されたデータ構造、つまりプレーンな.NETジェネリックに見つかることを思い出させましたDictionary<TKey, TValue>

1つではなく、多くのリンクリストが配列に格納されます。

  • 挿入/削除時に多くの小さな(デ)割り当てを行うのを避けます。
  • ハッシュテーブルの初期読み込みは、配列が順番に埋められるため、かなり高速です(CPUキャッシュで非常にうまく機能します)。
  • チェーンハッシュテーブルはメモリの点で高価であることは言うまでもありません。この "トリック"により、x64では "ポインタサイズ"が半分になります。

基本的に、リンクされたリストの多くは配列に格納されます。(使用されるバケットごとに1つ。)再利用可能なノードの無料リストは、それらの間で「織り交ぜられます」(削除があった場合)。配列は開始時/再ハッシュ時に割り当てられ、チェーンのノードはその中に保持されます。削除に続くフリーポインタ(配列へのインデックス)もあります。;-)だから-信じようと信じまいと-FORTRANのテクニックはまだ生きています。(...そして、最も一般的に使用される.NETデータ構造の1つよりも他にはありません;-)。


2
あなたが見逃した場合のために、ここにニールのコメントがあります:「人々(私を含め、申し訳ありませんが)は、FORTRAN IV(ポインタの概念がない)のような言語で、ポインタのないリンクリストを実装していました。 「実際の」メモリの代わりに配列を使用しました。」
Andras Vass

Dictionary.NETで大幅に保存する場合は、「配列内のリンクされたリスト」アプローチを追加する必要があります。そうしないと、各ノードでヒープに個別のオブジェクトが必要になり、ヒープに割り当てられたすべてのオブジェクトにオーバーヘッドが生じます。(en.csharp-online.net/Common_Type_System%E2%80%94Object_Layout
アンドラーシュVassの

std::listロックのないマルチスレッドのコンテキストでは、C ++のデフォルトは安全ではないことも知っておくとよいでしょう。
Mooing Duck 2013

49

リンクリストは、任意の長さ(コンパイル時には不明)の長さのリストに対して、多くの挿入や削除を行う必要があるが、検索を多く行う必要がない場合に非常に役立ちます。

(双方向リンク)リストの分割と結合は非常に効率的です。

リンクリストを組み合わせることもできます。たとえば、ツリー構造は、水平リンクリスト(兄弟)を接続する「垂直」リンクリスト(親/子関係)として実装できます。

これらの目的で配列ベースのリストを使用すると、重大な制限があります。

  • 新しいアイテムを追加することは、配列を再割り当てする必要があることを意味します(または、将来の拡張に備えて再割り当ての数を減らすために必要なスペースよりも多くのスペースを割り当てる必要があります)。
  • アイテムを削除すると、無駄なスペースが残ったり、再割り当てが必要になります
  • 末尾以外の場所にアイテムを挿入するには、1つの位置に多くのデータを(場合によっては再割り当てして)コピーします

5
質問はに減少して、時に行う序によって、リスト内の非常に多くのルックアップを使用すると、シーケンスの途中で挿入と削除の多くを実行する必要がありますが、ではありませんか?リンクされたリストをトラバースすることは、通常、配列をコピーすることと同じかそれ以上のコストがかかるため、配列内のアイテムの削除と挿入に関するすべてのことは、リスト内のランダムアクセスと同じくらい悪いことです。LRUキャッシュは、私が考えることができる1つの例です。途中でかなり削除する必要がありますが、リストを歩く必要はありません。
スティーブジェソップ

2
リストへの追加には、追加するすべての要素のメモリ割り当てが含まれます。これには、非常に高価なシステムコールが含まれる場合があります。配列に追加する必要があるのは、配列を拡張する必要がある場合のみです。実際、ほとんどの言語では(まさにこれらの理由により)配列が推奨されるデータ構造であり、リストはほとんど使用されていません。

1
どちらを想定しますか?その割り当てが驚くほど高速であることは明らかです。通常、オブジェクトサイズをポインタに追加する必要があります。GCの総オーバーヘッドは低いですか?前回実際のアプリで測定してみたときの要点は、プロセッサがアイドル状態のときにJavaがすべての作業を行っていたため、当然、目に見えるパフォーマンスにはそれほど影響しませんでした。ビジーCPUベンチマークでは、Javaを混乱させ、最悪のケースで最悪の割り当て時間を取得するのは簡単でした。ただし、これは何年も前のことであり、世代別ガベージコレクションにより、GCの総コストは大幅に削減されました。
スティーブジェソップ

1
@Steve:リストと配列の間で割り当てが「同じ」であるというのは間違っています。リストにメモリを割り当てる必要があるたびに、小さなブロック-O(1)を割り当てるだけです。配列の場合、リスト全体に十分な大きさの新しいブロックを割り当て、リスト全体をコピーする必要があります-O(n)。リストの既知の場所に挿入するには、固定数のポインターを更新します-O(1)。ただし、配列に挿入し、後のアイテムを1つ上の位置にコピーして、挿入のためのスペースを確保します-O(n)。したがって、配列の方がLLよりも効率が悪い場合が多くあります。
Jason Williams

1
@ジェリー:わかりました。私のポイントは、配列の再割り当てのコストの大部分はメモリの割り当てではなく、配列の内容全体を新しいメモリにコピーする必要があるということです。配列の項目0に挿入するには、配列の内容全体をメモリの1つ上の位置にコピーする必要があります。配列が悪いと言っているのではありません。ランダムアクセスが必要ない状況があり、LLの真に一定時間の挿入/削除/再リンクが望ましい状況があるだけです。
Jason Williams

20

リンクされたリストは非常に柔軟です。1つのポインターを変更するだけで、配列リストでは同じ操作が非常に非効率になるような大きな変更を加えることができます。


セットやマップではなくリストを使用する理由を動機づけることは可能でしょうか?
patrik

14

配列は、リンクリストが通常比較されるデータ構造です。

通常、リンクされたリストは、リスト自体に多くの変更を加える必要がある場合に役立ちますが、配列は要素への直接アクセスでリストよりも優れています。

ここでは、リストと配列で実行できる操作のリストを、相対的な操作コストと比較しています(n =リスト/配列の長さ)。

  • 要素を追加する:
    • リストでは、新しい要素とリダイレクトポインターにメモリを割り当てるだけです。O(1)
    • アレイでは、アレイを再配置する必要があります。オン)
  • 要素を削除する
    • リストではポインタをリダイレクトするだけです。O(1)。
    • 配列で、削除する要素が配列の最初または最後の要素でない場合、O(n)時間をかけて配列を再配置します。それ以外の場合は、ポインタを配列の先頭に再配置するか、配列の長さを減らすことができます
  • 既知の位置にある要素を取得する:
    • リストでは、最初の要素から特定の位置にある要素までリストを移動する必要があります。最悪の場合:O(n)
    • 配列では、要素にすぐにアクセスできます。O(1)

これは、これら2つの人気のある基本的なデータ構造の非常に低レベルの比較であり、リストに多くの変更を加える必要がある状況(要素の削除または追加)で、リストのパフォーマンスが向上することがわかります。一方、配列の要素に直接アクセスする必要がある場合、配列はリストよりもパフォーマンスが高くなります。

メモリ割り当ての観点からは、すべての要素を並べて配置する必要がないため、リストの方が優れています。一方、次の(または前の)要素へのポインタを格納するオーバーヘッドは(少し)あります。

これらの違いを知ることは、開発者が実装でリストと配列のどちらを選択するかにとって重要です。

これはリストと配列の比較であることに注意してください。ここで報告されている問題に対する適切な解決策があります(例:SkipLists、Dynamic Arraysなど)。この回答では、すべてのプログラマが知っておくべき基本的なデータ構造を考慮しました。


これは、リストの適切な実装と配列のひどい実装にいくらか当てはまります。ほとんどの配列の実装は、あなたがそれらを認めるよりもはるかに洗練されています。また、動的メモリの割り当てがどれほど高額になるかを理解していないと思います。

この回答は、データ構造大学コースのプログラムをカバーするものではありません。これは、リンクされたリストと配列を考慮して書かれた比較であり、あなた、私、そしてほとんどの人が知っている方法で実装されています。幾何学的に拡張する配列、スキップリストなどは私が知っているソリューションであり、私が使用し、研究していますが、より深い説明が必要であり、stackoverflowの答えには適合しません。
Andrea Zilio 2010年

1
「メモリ割り当ての観点から見ると、すべての要素を並べて配置する必要がないため、リストの方が優れています。」逆に、要素を互いに隣接させておくため、連続したコンテナの方が適しいます。最近のコンピューターでは、データの局所性が重要です。メモリの周りにジャンプすると、あなたのキャッシュのパフォーマンスを殺すことをすべて、および(効果的に)ランダムな位置にある要素を挿入しより高速なC ++のような動的配列で実行することをプログラムにリード線std::vectorなどC ++などのリンクリストに比べてstd::list、単に横断理由リストはとても高いです。
David Stone

@DavidStone多分私は十分に明確ではなかったかもしれませんが、その文では、要素を格納するために連続したスペースを必要としないという事実を言及していました。特に、小さすぎないものを保存したい場合や、利用可能なメモリが限られている場合は、データを保存するのに十分な連続した空き領域がない可能性がありますが、代わりにリストを使用してデータを合わせることができます(ポインターのオーバーヘッドがあります) ...両方のスペースと、先ほど述べたパフォーマンスの問題が原因です)。私はおそらくそれをより明確にするために私の答えを更新する必要があります。
Andrea Zilio、2012年

4

単一リンクリストは、セルアロケーターまたはオブジェクトプールの空きリストに適しています。

  1. 必要なのはスタックだけなので、単一リンクリストで十分です。
  2. すべてがすでにノードに分割されています。セルがポインターを含むのに十分な大きさであれば、侵入型リストノードの割り当てオーバーヘッドはありません。
  3. vectorまたはdequeは、ブロックごとに1つのポインターのオーバーヘッドを課します。これは、ヒープを最初に作成したとき、すべてのセルが解放されているため、重要です。そのため、事前にコストがかかります。最悪の場合、セルあたりのメモリ要件が2倍になります。

まあ、同意しました。しかし、何人のプログラマが実際にそのようなものを作成していますか?ほとんどは単にstd :: listなどが提供するものを再実装しているだけです。そして実際に「侵入型」は通常、指定したものとは少し異なる意味を持っています。つまり、可能な各リスト要素にはデータとは別のポインタが含まれています。

1
幾つ?0以上100万未満;-)ジェリーの質問は、「リストを上手に使用する」、「すべてのプログラマが日常的に使用するリストを上手に使用する」、またはその間に何かでしたか。リスト要素であるオブジェクト内に含まれるリストノードの「侵入的」以外の名前は知りません-ユニオンの一部として(C用語で)かどうか。ポイント3は、それを許可する言語(C、C ++、アセンブラーが良い)にのみ適用されます。Javaが悪い。
スティーブジェソップ

4

二重リンクリストは、要素(JavaのLinkedHashMap)の順序も定義するハッシュマップの順序を定義するのに適しています。

  1. 関連するベクターまたは両端キュー(1ではなく2ポインター)よりも多くのメモリオーバーヘッドが発生しますが、挿入/削除のパフォーマンスが向上します。
  2. とにかくハッシュエントリのノードが必要なので、割り当てオーバーヘッドはありません。
  3. 参照の局所性は、各オブジェクトをいずれかの方法でメモリにプルする必要があるため、ポインタのベクターまたは両端キューと比較して追加の問題ではありません。

確かに、より洗練された調整可能なものと比較して、最初にLRUキャッシュが良いアイデアであるかどうかについて議論することができますが、これを使用する場合、これはかなりまともな実装です。すべての読み取りアクセスでベクターまたは両端キューで中央から削除して最後に削除を実行する必要はありませんが、通常、ノードを末尾に移動することで問題ありません。


4

リンクされたリストは、データの保存場所を制御できない場合でも自然な選択肢の1つですが、何らかの方法でオブジェクト間を移動する必要があります。

たとえば、C ++でメモリ追跡を実装する場合(新規/削除置換)、解放されたポインターを追跡する制御データ構造が必要です。これは、完全に実装する必要があります。別の方法として、各データチャンクの先頭にリンクリストを割り当て、追加します。

deleteが呼び出されたときにリストのどこにいるかがすぐにわかるので、O(1)でメモリを簡単に放棄できます。また、mallocされたばかりの新しいチャンクを追加することもO(1)にあります。この場合、リストのウォークが必要になることはほとんどないため、O(n)のコストはここでは問題になりません(とにかく、構造のウォークはO(n)です)。


3

高速のプッシュ、ポップ、ローテーションが必要で、O(n)インデックスを気にしない場合に便利です。


(たとえば)dequeと比較して、C ++リンクリストの時間を気にしたことがありますか?

@ニール:私が持っているとは言えない。
Ignacio Vazquez-Abrams

@ニール:C ++がリンクリストクラスを故意に妨害して、他のどのコンテナーよりも遅くする(真実から遠くない)場合、言語にとらわれない質問とどう関係するのでしょうか。侵入リンクリストは、リンクリストのままです。
Steve Jessop、2010年

@Steve C ++は言語です。どうして意欲があるのか​​わかりません。C ++委員会のメンバーがリンクリストを妨害することを示唆している場合(多くの操作では論理的に遅いはずです)、有罪の男性に名前を付けてください!

3
これは実際には妨害ではありません。外部リストノードには利点がありますが、パフォーマンスはその1つではありません。ただし、あなたが気付いているのと同じことをトレードオフするときは、誰もが気付いていたはずです。つまり、をうまく活用するのは非常に難しいということですstd::list。煩雑なリストは、コンテナ要素の最小要件というC ++の哲学に適合しません。
Steve Jessop、2010年

3

一重リンクリストは、関数型プログラミング言語における一般的な「リスト」データ型の明らかな実装です。

  1. 頭への追加は速く、(append (list x) (L))そして(append (list y) (L))、ほぼすべてのデータの共有することができます。書き込みのない言語でのコピーオンライトの必要はありません。関数型プログラマーは、これを利用する方法を知っています。
  2. テールへの追加は残念ながら遅いですが、他の実装ではそうなります。

比較すると、ベクターまたは両端キューは通常、両端に追加するのが遅く、リスト(ベクター)全体、またはインデックスブロックとデータブロックのコピーを取得する必要があります(少なくとも2つの異なる追加の例)。 (deque)に追加されます。実際、何らかの理由で末尾に追加する必要のある大きなリストの両端キューについて、そこに言うべきことがあります。関数型プログラミングについて判断するのに十分な情報がありません。


3

リンクリストの適切な使用例の1つは、リストの要素が非常に大きい場合です。1つまたは2つだけが同時にCPUキャッシュに収まるのに十分な大きさ。この時点で、反復のためのベクトルや配列などの連続したブロックコンテナーが持つ利点は多かれ少なかれ無効化されており、多くの挿入と削除がリアルタイムで発生している場合は、パフォーマンス上の利点が得られる可能性があります。


2

私の経験から、疎行列とフィボナッチヒープの実装。リンクリストを使用すると、このようなデータ構造の全体的な構造をより詳細に制御できます。スパースマトリックスがリンクリストを使用して最適に実装されているかどうかはわかりませんが、おそらくもっと良い方法がありますが、それは学部卒のCSでリンクリストを使用してスパースマトリックスの入出力を学習するのに役立ちました:)


1

リストのO(1)であり、他のデータ構造でO(1)に実装するのは非常に難しい2つの補完的な操作があります-要素の順序を維持する必要がある場合、任意の位置から要素を削除および挿入します。

ハッシュマップはO(1)で挿入と削除を実行できますが、要素を順番に繰り返すことはできません。

上記の事実を踏まえると、ハッシュマップをリンクリストと組み合わせて、気の利いたLRUキャッシュを作成できます。一定数のキーと値のペアを格納し、最も最近アクセスされていないキーを削除して新しいキーのためのスペースを作るマップ。

ハッシュマップのエントリには、リンクリストノードへのポインタが必要です。ハッシュマップにアクセスすると、リンクリストノードは現在の位置からリンク解除され、リストの先頭に移動します(O(1)、リンクリストの場合はそうです!)。最も最近使用されていない要素を削除する必要がある場合、リストの末尾から1つを削除する必要があります(ここでも、末尾ノードへのポインターを保持していると仮定してO(1))を、関連するハッシュマップエントリと共に(したがって、ハッシュマップのリストが必要です。)


1

リンクリストは、繰り返しと連動するパーツを含むシステムのドメイン駆動設計スタイルの実装に非常に役立つ可能性があることを考慮してください。

頭に浮かぶ例は、吊り下げたチェーンをモデリングする場合です。特定のリンクのテンションを知りたい場合は、インターフェイスに「見かけの」重みのゲッターを含めることができます。その実装には、次のリンクに見かけの重みを要求するリンクが含まれ、結果に独自の重みが追加されます。この方法では、最下部までの全長が、チェーンのクライアントからの1回の呼び出しで評価されます。

自然言語のように読み取るコードの支持者である私は、これによってプログラマーがチェーンリンクにどれほどの重みを課すかを尋ねることができるのが好きです。また、リンク実装の境界内でこれらのプロパティの子を計算するという懸念を維持し、チェーンの重み計算サービスの必要性を排除します。


1

メッシュや画像処理、物理エンジン、レイトレーシングなどのパフォーマンスが重要なフィールドで機能するリンクリストで最も役立つケースの1つは、リンクリストを使用すると、参照の局所性が実際に向上し、ヒープ割り当てが減少し、場合によってはメモリ使用量が減少することもあります。簡単な選択肢。

これは、リンクリストがその逆を頻繁に行うことで悪名高いため、それをすべて実行できる完全な矛盾のように見えるかもしれませんが、各リストノードには固定サイズと配置要件があるという点で固有のプロパティがあり、これを利用してそれらは隣接して格納され、可変サイズのものではできない方法で一定時間に削除されます。

結果として、100万のネストされた可変長サブシーケンスを含む可変長シーケンスを格納することと同様のことを実行したい場合を考えてみましょう。具体的な例は、100万のポリゴン(一部の三角形、一部の四角形、一部の五角形、一部の六角形など)を格納するインデックス付きメッシュであり、メッシュ内の任意の場所からポリゴンが削除されたり、ポリゴンが再構築されて既存のポリゴンに頂点を挿入したり、 1つ削除します。その場合、100万のtinyを格納するとstd::vectors、すべてのベクトルへのヒープ割り当てと、爆発的なメモリ使用に直面することになります。100万タイニーSmallVectorsは、一般的な場合ほどこの問題に悩まされることはありませんが、個別にヒープに割り当てられていない事前割り当てされたバッファーは、爆発的なメモリ使用を引き起こす可能性があります。

ここでの問題は、100万のstd::vectorインスタンスが100万の可変長のものを格納しようとすることです。可変長のものは、連続して非常に効果的に格納できず、コンテンツをヒープの他の場所に格納しなかった場合、一定の時間で(少なくとも非常に複雑なアロケーターなしで簡単に)削除できないため、ヒープ割り当てが必要になる傾向があります。

代わりに、これを行う場合:

struct FaceVertex
{
    // Points to next vertex in polygon or -1
    // if we're at the end of the polygon.
    int next;
    ...
};

struct Polygon
{
     // Points to first vertex in polygon.
    int first_vertex;
    ...
};

struct Mesh
{
    // Stores all the face vertices for all polygons.
    std::vector<FaceVertex> fvs;

    // Stores all the polygons.
    std::vector<Polygon> polys;
};

...次に、ヒープ割り当てとキャッシュミスの数を劇的に減らしました。アクセスするすべてのポリゴンに対してヒープ割り当てと潜在的に強制的なキャッシュミスを要求する代わりに、メッシュ全体に格納された2つのベクトルの1つが容量(償却コスト)を超えた場合にのみヒープ割り当てを要求するようになりました。また、ある頂点から次の頂点に到達するためのストライドによって、キャッシュミスのシェアが引き続き発生する可能性がありますが、ノードが隣接して格納され、隣接する頂点が格納される可能性があるため、すべてのポリゴンが個別の動的配列を格納する場合よりも少ないことがよくあります。立ち退きの前にアクセスする(特に、多くのポリゴンが頂点を一度に追加するため、ライオンのポリゴンの頂点の割合が完全に連続することを考慮します)。

次に別の例を示します。

ここに画像の説明を入力してください

...グリッドセルを使用して、たとえば、1フレームごとに移動する1600万個のパーティクルのパーティクル間衝突を加速します。そのパーティクルグリッドの例では、リンクリストを使用して、3つのインデックスを変更するだけで、あるグリッドセルから別のセルにパーティクルを移動できます。ベクトルから消去して別のベクトルにプッシュバックすると、かなりのコストがかかり、ヒープ割り当てが増える可能性があります。リンクリストは、セルのメモリを32ビットに減らします。ベクトルは、実装に応じて、空のベクトルに32バイトを使用できるポイントに動的配列を事前に割り当てることができます。グリッドセルが約100万個ある場合、それはかなりの違いです。

...そしてここで私は最近リンクリストが最も役立つと思います。特に32ビットインデックスは64ビットマシンのリンクのメモリ要件を半分にし、それらがノードは配列に連続して格納されます。

多くの場合、それらをインデックス付きのフリーリストと組み合わせて、どこでも一定の時間で削除および挿入できるようにします。

ここに画像の説明を入力してください

その場合、nextノードが削除されている場合、インデックスは次の空きインデックスを指すか、ノードが削除されていない場合、次に使用されるインデックスを指します。

そして、これは最近私がリンクされたリストで見つける一番のユースケースです。たとえば、それぞれ4つの要素(たとえば、要素が削除され、これらのサブシーケンスの1つに追加されている場合もある)を平均化する100万の可変長サブシーケンスを保存する場合、リンクされたリストにより、400万を保存できます。リンクされたリストノードは、それぞれ個別にヒープ割り当てされた100万のコンテナではなく、連続して1つの巨大なベクトル、つまり100万の小さなベクトルではありません。


0

過去にC / C ++アプリケーションでリンクリスト(二重リンクリストも含む)を使用しました。これは.NETより前のバージョンであり、stlでさえありました。

リンクされたリストを.NET言語で使用することはおそらくないでしょう。必要な走査コードはすべてLinq拡張メソッドを介して提供されるからです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.