なぜ誰かが配列上でリンクリストを使用したいのですか?
リンクリストをコーディングすることは、間違いなく、配列を使用するよりも少し手間がかかります。
リンクリストでは新しい要素の挿入は簡単だと思いますが、配列では面倒な作業です。リンクされたリストを使用して一連のデータを保存することと、配列に保存することには、他の利点がありますか?
この質問は この質問 の複製ではありません。これは、他の質問が特定のJavaクラスについて具体的に尋ねている一方で、この質問が一般的なデータ構造に関係しているためです。
もう1つの正当な理由は、リンクリストが効率的なマルチスレッド実装に適していることです。この理由は、変更はローカルになりがちであるためです。データ構造のローカライズされた部分での挿入と削除のポインタは1つまたは2つだけです。そのため、同じリンクリストで多数のスレッドを動作させることができます。さらに、CASタイプの操作を使用してロックフリーバージョンを作成し、重いロックを完全に回避することができます。
リンクリストを使用すると、イテレータは変更が行われている間にリストを走査することもできます。変更が衝突しない楽観的なケースでは、イテレーターは競合することなく続行できます。
配列の場合、配列のサイズを変更するには、配列の大部分をロックする必要がある可能性が高く、実際、配列全体でグローバルロックを使用せずにこれを行うことはめったにないため、変更によって世界情勢が停止します。
ウィキペディアには、違いに関する非常に良いセクションがあります。
リンクリストには、配列よりもいくつかの利点があります。要素はリンクリストに無期限に挿入できますが、配列は最終的にいっぱいになるか、サイズを変更する必要があります。これは、メモリが断片化されている場合には不可能な高価な操作です。同様に、多くの要素が削除された配列は、無駄に空になるか、小さくする必要があります。
一方、配列はランダムアクセスを許可しますが、リンクリストは要素への順次アクセスのみを許可します。実際、単一リンクリストは一方向にのみ移動できます。このため、リンクリストは、ヒープソートなど、インデックスで要素をすばやく検索するのに役立つアプリケーションには適していません。また、参照キャッシュとデータキャッシュの局所性により、多くのマシンのリンクリストよりも配列のシーケンシャルアクセスが高速です。リンクリストには、キャッシュの利点はほとんどありません。
リンクリストのもう1つの欠点は、参照に余分なストレージが必要になることです。これにより、文字やブール値などの小さなデータ項目のリストでは参照が実用的でないことがよくあります。また、新しい要素ごとにメモリを個別に割り当てることは、無駄であり、無駄なアロケータを使用すると遅くなる場合があります。これは、一般的にメモリプールを使用して解決される問題です。
別のリストを追加します-リストは 純粋に機能的 データ構造として機能します。
たとえば、同じ終了セクションを共有する完全に異なるリストを持つことができます
a = (1 2 3 4, ....)
b = (4 3 2 1 1 2 3 4 ...)
c = (3 4 ...)
すなわち:
b = 4 -> 3 -> 2 -> 1 -> a
c = a.next.next
a
が指すデータをb
およびc
にコピーする必要はありません。
これが、不変変数を使用する関数型言語で非常に人気がある理由です-prepend
およびtail
操作は、元のデータをコピーせずに自由に実行できます-データを不変として扱う場合の非常に重要な機能です。
2つのリンクリスト(特に2つの二重リンクリスト)のマージは、2つの配列のマージよりもはるかに高速です(マージが破壊的であると仮定した場合)。前者はO(1)を取り、後者はO(n)を取ります。
編集:明確にするために、ここではマージソートのようにではなく、順序付けられていない意味で「マージ」を意味しました。おそらく、「連結」する方がより良い言葉だったでしょう。
リストの中央に挿入するのが簡単であることに加えて、配列よりもリンクされたリストの中央から削除する方がはるかに簡単です。
しかし、率直に言って、リンクリストを使用したことはありません。高速な挿入と削除が必要なときはいつでも、高速なルックアップも必要だったため、HashSetまたはDictionaryに移動しました。
まず第一に、C++のリンクリストは、配列よりも作業するのにそれほど苦労するべきではありません。リンクリストには std :: list または boost pointer list を使用できます。リンクリストと配列の主な問題は、ポインターとひどいランダムアクセスに必要な余分なスペースです。リンクリストを使用する必要がある場合
ArrayListおよびLinkedListに対して広く評価されていない引数は、デバッグ中にLinkedListは不快ですです。メンテナンス開発者がプログラムを理解するために費やした時間。バグ、増加、およびIMHOを見つけるために、エンタープライズアプリケーションでのパフォーマンス向上のナノ秒またはメモリ消費のバイトを正当化できない場合があります。場合によっては(もちろん、アプリケーションの種類によって異なります)、数バイトを無駄にした方がよいのですが、より保守しやすい、または理解しやすいアプリケーションを用意する方が良いでしょう。
たとえば、Java環境でEclipseデバッガーを使用して、ArrayListをデバッグすると、非常にわかりやすい構造が明らかになります。
arrayList ArrayList<String>
elementData Object[]
[0] Object "Foo"
[1] Object "Foo"
[2] Object "Foo"
[3] Object "Foo"
[4] Object "Foo"
...
一方、LinkedListのコンテンツを見て特定のオブジェクトを見つけることは、LinkedList内部を除外するために必要な認知オーバーヘッドは言うまでもなく、Expand-The-Treeをクリックする悪夢になります。
linkedList LinkedList<String>
header LinkedList$Entry<E>
element E
next LinkedList$Entry<E>
element E "Foo"
next LinkedList$Entry<E>
element E "Foo"
next LinkedList$Entry<E>
element E "Foo"
next LinkedList$Entry<E>
previous LinkedList$Entry<E>
...
previous LinkedList$Entry<E>
previous LinkedList$Entry<E>
previous LinkedList$Entry<E>
私にとってはこんな感じ
アクセス
ストレージ
サイズ
挿入/削除
2つのこと:
リンクされたリストをコーディングすることは、間違いなく、配列を使用するよりも少し手間がかかり、彼は何が追加の努力を正当化するのか疑問に思いました。
C++を使用する場合は、リンクリストをコーディングしないでください。 STLを使用するだけです。実装がどれほど難しいかは、ほとんどのデータ構造がすでに実装されているため、あるデータ構造を別のデータ構造よりも選択する理由にはなりません。
配列とリンクリストの実際の違いについては、構造の使用をどのように計画するかが大きなポイントです。ベクトルという用語は、C++のサイズ変更可能な配列の用語なので使用します。
リンクリストへのインデックス付けは、指定されたインデックスに到達するためにリストを走査する必要があるため低速です。一方、ベクトルはメモリ内で連続しており、ポインター計算を使用してそこに到達できます。
リンクリストの末尾または先頭に追加するのは簡単です。1つのリンクを更新するだけで済み、ベクターではコンテンツのサイズを変更してコピーする必要がある場合があるためです。
リストから項目を削除するのは簡単です。リンクのペアを解除してから、それらを再び接続するだけです。順序を気にするかどうかに応じて、ベクターからアイテムを削除する速度は速くも遅くもなります。削除するアイテムの上にある最後のアイテムをスワップインするのは高速ですが、ダウンした後にすべてをシフトするのは遅くなりますが、順序は維持されます。
Eric Lippertは最近、配列を保守的に使用する理由の1つに post を付けました。
確かに、高速な挿入と削除は、リンクリストの最適な引数です。構造が動的に成長し、任意の要素(動的スタックやキューなど)への一定時間のアクセスが必要ない場合は、リンクリストが適切な選択です。
リストの中央から追加および削除する以外に、リンクリストは動的に拡大および縮小できるため、リンクリストの方が好きです。
リンクリストは、コレクションが絶えず成長および縮小している場合に特に役立ちます。たとえば、配列を使用してキューを実装しようとする(最後に追加し、前面から削除する)ことを想像するのは困難です。一方、リンクリストでは簡単です。
独自のリンクリストをコーディングする人はもういません。それはばかげている。リンクリストを使用するとより多くのコードが必要になるという前提は、間違っています。
最近では、リンクされたリストを作成することは、学生が概念を理解できるようにするための演習にすぎません。代わりに、全員が事前に作成されたリストを使用します。 C++では、質問の説明に基づいて、おそらくstlベクトル(#include <vector>
)を意味します。
したがって、リンクリストと配列のどちらを選択するかは全体アプリのニーズに応じて各構造の異なる特性を比較することです。追加のプログラミングの負担を克服しても、決定に影響はありません。
ここに簡単なものがあります:アイテムの削除はより速くなります。
それは本当に効率の問題であり、リンクされたリスト内の要素を挿入、削除、または移動するオーバーヘッドは最小限です。つまり、操作自体はO(1)で、O(n)配列の場合。データのリストを頻繁に操作している場合、これは大きな違いを生む可能性があります。どのように操作するかに基づいてデータ型を選択し、使用しているアルゴリズムに最も効率的なデータ型を選択しました。
配列は、アイテムの正確な数がわかる場所、およびインデックスによる検索が意味をなす場所に意味があります。たとえば、圧縮せずに特定の瞬間のビデオ出力の正確な状態を保存したい場合、おそらくサイズ[1024] [768]の配列を使用します。これにより、必要なものが正確に提供されます。また、指定されたピクセルの値を取得するためのリストは非常に遅くなります。配列が意味をなさない場所では、データを効果的に処理するために、一般的にリストよりも優れたデータ型があります。
配列とリンクリスト:
リンクリストは、配列よりも維持するオーバーヘッドが多く、これらすべての点が合意されている追加のメモリストレージも必要です。しかし、配列ができないことはいくつかあります。多くの場合、長さ10 ^ 9の配列が必要だと仮定します。連続したメモリ位置を1つ取得する必要があるため、取得できません。リンクリストはここで救世主になる可能性があります。
複数のデータをデータと共に保存したい場合、それらをリンクリストで簡単に拡張できます。
通常、STLコンテナには、背後でリンクリストの実装があります。
配列では、O(1)時間内に任意の要素にアクセスする特権があります。そのため、バイナリ検索のクイックソートなどの操作に適しています。一方、リンクリストは、O(1)時間のように挿入削除に適しています。どちらにも長所と短所があり、実装したいものに合わせて、どちらか一方を優先することです。
-より大きな問題は、両方のハイブリッドを使用できるかどうかです。 pythonとPerlがリストとして実装するもののようなもの。
順序付けされたセットがあり、要素を追加および削除することで変更することもできます。さらに、後で前または次の要素を取得できるように、要素への参照を保持する機能が必要です。たとえば、To Doリストや本の段落セット。
最初に、セット自体の外部のオブジェクトへの参照を保持する場合、オブジェクト自体を保存するのではなく、配列にポインターを保存することになります。そうしないと、配列に挿入できません。オブジェクトが配列に埋め込まれている場合、オブジェクトは挿入中に移動し、それらへのポインターは無効になります。配列インデックスについても同様です。
あなたが最初に指摘したように、あなたの最初の問題は挿入です-リンクされたリストはO(1)への挿入を許可しますが、配列は通常O(n)を必要とします。この問題は部分的に克服できます-読み取りと書き込みの両方が最悪の場合、対数である配列のような通常のアクセスインターフェイスを提供するデータ構造を作成することが可能です。
2番目の、より深刻な問題は、次の要素を見つける要素がO(n)であるということです。セットが変更されていない場合、ポインタの代わりに要素のインデックスを参照として保持することができます。したがって、find-nextをO(1)操作にしますが、それはすべてオブジェクト自体であり、「配列」全体をスキャンする以外に、配列内の現在のインデックスを特定する方法はありません。これは配列にとって乗り越えられない問題です-挿入を最適化できたとしても、次のタイプの検索操作を最適化するためにできることは何もありません。
リンクリスト
挿入に関しては、より望ましいです!基本的には、ポインターを処理することです
1-> 3-> 4
挿入(2)
1 ........ 3 ...... 4
..... 2
最後に
1-> 2-> 3-> 4
3の2点からの1つの矢印と2の1点の矢印
シンプル!
しかし、配列から
| 1 | 3 | 4 |
挿入(2)| 1 | 3 | | 4 | | 1 | | 3 | 4 | | 1 | 2 | 3 | 4 |
誰でも違いを視覚化できます! 4つのインデックスに対して、3つのステップを実行しています
配列の長さが100万の場合はどうなりますか?配列は効率的ですか?答えはノーだ! :)
削除についても同じことが言えます!リンクリストでは、単純にポインタを使用して、要素を無効にし、次にオブジェクトクラス内で無効にすることができます。しかし、配列の場合、shiftLeft()を実行する必要があります
お役に立てば幸いです! :)
配列は本質的に静的であるため、メモリ割り当てなどのすべての操作はコンパイル時にのみ発生します。そのため、プロセッサは実行時の労力を減らす必要があります。
リンクリストを使用する唯一の理由は、要素の挿入が簡単なことです(削除も可能です)。
欠点は、ポインターが多くのスペースを取ることです。
そして、そのコーディングについては難しいです:通常、コードリンクリストは必要ありません(または一度だけ) STL に含まれており、それをしなければならないのであればそれほど複雑ではありません。
なぜ誰かが配列上でリンクリストを使用したいのですか?
これは1つの理由にすぎません-リンクリストデータ構造が必要で、使用しているプログラミング言語がポインターをサポートしていない場合。
また、リンクリストは配列よりも優れていると思います。私たちはリンクリストを走査しますが、配列は走査しないので
挿入と削除の利便性に加えて、リンクリストのメモリ表現は配列とは異なります。リンクリストの要素の数に制限はありませんが、配列では、要素の総数を指定する必要があります。 this の記事を確認してください。
なぜ配列上のリンクリストですか?すでにいくつかの人が言ったように、挿入と削除の速度が向上しました。
しかし、どちらか一方の限界に合わせて生きる必要はないかもしれませんし、両方の利点を同時に得る必要はありません...
配列を削除するには、「Deleted」バイトを使用して、行が削除されたという事実を表すことができます。したがって、配列を再編成する必要はなくなりました。挿入の負担を軽減したり、データを急速に変更したりするには、リンクリストを使用します。次に、それらを参照する場合、最初にロジックを検索し、次にもう一方を検索します。したがって、それらを組み合わせて使用すると、両方の長所が得られます。
本当に大きな配列がある場合は、それを別のはるかに小さな配列またはリンクされたリストと組み合わせて、小さい配列に20、50、100個の最近使用されたアイテムを保持できます。必要なものが短いリンクリストまたは配列にない場合は、大きな配列に移動します。そこに見つかった場合は、「最近使用したものが再使用される可能性が最も高い」という前提で、リンクされた小さいリスト/配列に追加できます(そして、はい、リストから最も最近使用されていない項目をバンプする可能性があります)。これは多くの場合に当てはまり、.ASPセキュリティパーミッションチェックモジュールで取り組む必要があった問題を、簡単に、優雅に、そして印象的な速度で解決しました。
多くの人がリンクリストと配列の主な進歩/不一致に触れましたが、比較のほとんどは、一方が他方より優れているか悪いかです。配列でランダムアクセスを行うことはできますが、リンクリストなどではできません。ただし、これはリンクリストと配列が同様のアプリケーションに適用されることを前提としています。ただし、正しい答えは、特定のアプリケーションの展開において、アレイよりもリンクリストを優先する方法、またはその逆である必要があります。辞書アプリケーションを実装する場合、何を使用しますか?配列:mmmバイナリ検索やその他の検索アルゴリズムを使用して簡単に検索できますが、リンクリストの改善方法を考えてみましょう。辞書で「Blob」を検索するとします。 A-> B-> C-> D ----> Zのリンクリストがあり、各リスト要素がその文字で始まるすべての単語の配列または別のリストも指しているのは理にかなっていますか..
A -> B -> C -> ...Z
| | |
| | [Cat, Cave]
| [Banana, Blob]
[Adam, Apple]
さて、上記のアプローチの方が良いでしょうか、それとも[Adam、Apple、Banana、Blob、Cat、Cave]のフラット配列ですか?配列でも可能でしょうか?したがって、リンクリストの主な利点は、次の要素だけでなく、他のリンクリスト/配列/ヒープ/または他のメモリの場所を指す要素を持つことができることです。配列は、格納する要素のブロックサイズにスライスされた1つのフラットな連続メモリです。一方、リンクリストは、非連続メモリユニットのチャンク(任意のサイズで任意のデータを格納できます)で、それぞれを指します。あなたが望む他の方法。同様に、USBドライブを作成しているとしましょう。ファイルを配列またはリンクリストとして保存しますか?私が何を指しているのか、あなたの考えがわかると思います:)
言語に応じて、これらの短所と長所のいくつかを検討できます。
Cプログラミング言語:リンクリストを使用する場合(通常は構造体ポインターを使用)、メモリリークがないことを特別に考慮する必要があります。前述したように、リンクリストは簡単にシャッフルできます。これは、すべてがポインターを変更しているためです。しかし、すべてを解放することを忘れないでください。
Java:Javaには自動ガベージコレクションがあるため、メモリリークは問題になりませんが、リンクされたリストの実装の詳細は、高レベルのプログラマからは見えません。リストの中央からノードを削除するなどの方法は、言語の一部のユーザーが期待するよりも手順が複雑です。
配列とリンクリストの違いは、配列はインデックスベースのデータ構造であり、すべての要素がインデックスに関連付けられているのに対して、リンクリストは参照を使用するデータ構造であり、各ノードは別のノードを参照することです。配列のサイズは固定されていますが、リンクリストのサイズは固定されていません。
Linklistを使用する人は読む必要があります。人々は再びアレイに恋をするでしょう。 Out Of Order実行、ハードウェアプリフェッチ、メモリレイテンシなどについて説明します。
http://www.futurechips.org/thoughts-for-researchers/quick-post-linked-lists.html