配列/配列リストでリンクリストを使用するのはいつですか?


176

私は多くのリストと配列を使用していますが、配列リストをリンクリストと同じくらい簡単には使用できないというシナリオにまだ出会っていません。リンクされたリストが特に優れているときの例を誰かが教えてくれることを願っていました。


Javaでは、ArrayListとLinkedListはコンストラクタ以外はまったく同じコードを使用します。「配列リスト...リンクリストと同じくらい簡単に、または簡単に使用」は意味がありません。LinkedListより「簡単」なArrayListの例を提供してください。
S.Lott、2008

2
これをチェックだけでなく、stackoverflow.com/questions/322715/...
NONAME


3
S.Lottそれは真実ではありません。Java ArrayListは、いくつかのユーティリティ関数が追加された、Arrayのラッパーです。リンクされたリストは、明らかに、リンクされたリストです。 developer.classpath.org/doc/java/util/ArrayList-source.html
kingfrito_5005

回答:


261

リンクされたリストは、次の場合に配列よりも適しています。

  1. リストからの一定時間の挿入/削除が必要である(時間の予測可能性が絶対的に重要であるリアルタイムコンピューティングなど)

  2. リストにいくつのアイテムが含まれるかはわかりません。配列の場合、配列が大きくなりすぎると、メモリを再宣言してコピーする必要がある場合があります。

  3. 要素にランダムにアクセスする必要はありません

  4. リストの中央に項目を挿入できるようにしたい(優先キューなど)

配列は次の場合に適しています。

  1. 要素へのインデックス付き/ランダムアクセスが必要

  2. 配列の要素数が事前にわかっているため、配列に適切な量のメモリを割り当てることができます

  3. すべての要素を順番に反復するときは速度が必要です。配列でポインター計算を使用して各要素にアクセスできますが、リンクリストの各要素のポインターに基づいてノードを検索する必要があるため、ページフォールトが発生し、パフォーマンスが低下する可能性があります。

  4. メモリが問題です。塗りつぶされた配列は、リンクされたリストよりもメモリを消費しません。配列の各要素は単なるデータです。各リンクリストノードには、データと、リンクリスト内の他の要素への1つ(または複数)のポインターが必要です。

配列リスト(.Netのものと同様)は配列の利点を提供しますが、リソースを動的に割り当てます。これにより、リストのサイズをあまり気にする必要がなく、あらゆるインデックスでアイテムを削除することができます。要素をシャッフルします。パフォーマンスの点で、arraylistはraw配列よりも低速です。


7
良いスタートですが、これは重要なことを除外します:リストは構造の共有をサポートし、配列はより密集し、より局所性があります。
ダライアスベーコン

1
実際には、arraylistと配列のパフォーマンスの違いはごくわずかです。これは、同等のものを比較することを前提としています。たとえば、事前にサイズがわかっている場合は、それについてarraylistに伝えます。
11

40
LinkedListにO(1)挿入/削除があるのはいつからですか(これは、一定時間の挿入/削除と言ったときに私が思うことです)?LinkedListの途中にコンテンツを挿入することは常にO(n)です
Pacerier

28
LinkedListsには、(イテレータを介して)挿入の場所にすでにいる場合、O(1)挿入があります。ただし、常にではありません。
アダム

4
プライオリティキューにリンクリストを使用することは、非常に愚かな考えです。動的配列対応ヒープは、O(lg n)の償却済み挿入と最悪の場合の対数削除分を考慮に入れており、最も高速で実用的な優先度キュー構造の1つです。
Fred Foo 2013年

54

配列にはO(1)のランダムアクセスがありますが、ものを追加したり、削除したりするには非常にコストがかかります。

リンクされたリストは、アイテムをどこにでも追加または削除して反復するのに非常に安価ですが、ランダムアクセスはO(n)です。


3
アレイの端から項目を除去することからアイテムを削除/挿入されているように、Contantを時間であるのいずれかでリンクされたリストの終わり。真ん中...どちらでもない。
Joey

1
@JoeyはリンクリストO(n)の最後に挿入/削除されていませんか?最後から2番目のリンクに位置していない限り、最後の要素を見つけるためにO(n)ステップが必要です。
Alex Moore-Niemi 2015

@ AlexMoore-Niemi:単一リンクリストについては、はい。しかし、多くは前後にリンクを持っているため、どちらかの端へのポインタを保持します。
Joey

2
二重にリンクされたリストがあると、LLが値を順序付けしていない限り、前方と後方の検索を行うことになります...それでも最悪のシナリオはO(n)です
securecurve

「リンクされたリストは、どこにでもアイテムを追加または削除したり、繰り返し処理したりするのに非常に安価です」とは、完全には当てはまりません。リンクされたリストの真ん中にあるアイテムを削除したい場合は、リスト内のそのアイテムに到達するまで、最初から繰り返す必要があります。そのO(n / 2)時間。ここで、n =リスト内の項目の数。あなたの答えから、それは配列のように一定の時間O(1)を提案しているように聞こえます。リンクされたリストのヘッド/ルートノードを追加/削除するのは一定の時間です。
Yawar Murtaza、

21
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

ArrayListsは、追記型またはアペンダーには適していますが、フロントまたはミドルからの追加/削除には適していません。


14

他の回答に追加するために、ほとんどの配列リスト実装は、リストの最後に追加の容量を予約しているため、新しい要素をO(1)時間でリストの最後に追加できます。配列リストの容量を超えると、新しい大きな配列が内部的に割り当てられ、古い要素がすべてコピーされます。通常、新しい配列は古い配列の2倍のサイズです。これは、平均して、これらの実装では、、配列リストの最後に新しい要素を追加することはO(1)操作です。したがって、要素の数が事前にわからない場合でも、最後に要素を追加する限り、要素を追加するためのリンクリストよりも配列リストの方が高速になる可能性があります。明らかに、配列リストの任意の場所に新しい要素を挿入することは、まだO(n)操作です。

配列リスト内の要素へのアクセスは、アクセスがシーケンシャルであっても、リンクリストよりも高速です。これは、配列要素が連続したメモリに格納され、簡単にキャッシュできるためです。リンクリストノードは、多くの異なるページに分散している可能性があります。

任意の場所でアイテムを挿入または削除することがわかっている場合のみ、リンクリストを使用することをお勧めします。配列リストは、他のほとんどすべてに対して高速になります。


1
さらに、動的配列を使用して(抽象データ型の意味で)リンクリストを実装することもできます。このようにして、リストの先頭で一定時間の挿入と削除を償却しながら、また、その後に挿入する必要がある要素のインデックスがある場合にリストの中央で一定時間の挿入と削除を償却しながら、コンピューターキャッシュを活用できます。完了するか、削除する要素のインデックス(シフト/シフト解除は不要)。これの良いリファレンスはCLRS 10.3です。
ドメニコデフェリーチェ2014

7

リストの利点は、途中でアイテムを挿入する必要があり、配列のサイズ変更や移動を開始したくない場合に発生します。

これは通常そうではないという点で正しいです。そのような非常に特殊なケースがいくつかありましたが、多すぎませんでした。


配列のシフトとサイズ変更は、途中で反転を行うときに実際に行われることです。償却限度に達しない場合は、サイズ変更せずにシフトするだけで済みます。
securecurve 2016年

3

それはすべて、反復中に実行している操作のタイプに依存します。すべてのデータ構造は時間とメモリの間でトレードオフがあり、ニーズに応じて適切なDSを選択する必要があります。そのため、LinkedListが配列より高速である場合や、その逆の場合があります。データ構造に関する3つの基本的な操作を検討してください。

  • 検索中

配列はインデックスベースのデータ構造であるため、array.get(index)はO(1)時間かかりますが、linkedlistがインデックスDSではないため、indexまで走査する必要があります。ここで、インデックス<= n、nはリンクリストのサイズです。そのため、要素のランダムアクセスがある場合、配列はリンクリストの方が高速です。

Q.では、この背後にある美しさは何ですか?

配列は連続したメモリブロックであるため、最初のアクセス時にそれらの大きなチャンクがキャッシュに読み込まれます。これにより、配列の残りの要素にアクセスするのが比較的速くなり、参照の配列局所性の要素にアクセスするため、キャッチが少なくなります。ミス、キャッシュの局所性とは、キャッシュ内にある操作を指すため、メモリ内よりもはるかに高速に実行されます。基本的に、配列では、キャッシュ内にある順次要素アクセスの可能性を最大化します。リンクされたリストは必ずしもメモリの連続したブロックにあるとは限りませんが、リストに順番に現れる項目が実際にメモリ内で互いに近くに配置される保証はありません。これはキャッシュヒットが少ないことを意味します。

  • 挿入

配列と比較してLinkedList(Java)での挿入がO(1)操作であるため、これはLinkedListで簡単かつ高速です。配列がいっぱいの場合を考えてください。配列がいっぱいになった場合、新しい配列に内容をコピーする必要があります。最悪の場合、要素をO(n)のArrayListに追加しますが、配列の最後以外の場所に何かを挿入した場合、ArrayListもそのインデックスを更新する必要があります。リンクされたリストの場合、サイズを変更する必要はありません。ポインタを更新します。

  • 削除

これは挿入のように機能し、配列よりもLinkedListの方が優れています。


2

これらは、コレクションの最も一般的に使用される実装です。

配列リスト:

  • 最後に挿入/削除一般的にO(1)最悪の場合O(n)

  • 真ん中に挿入/削除O(n)

  • 任意の位置O(1)を取得する

LinkedList:

  • 任意の位置O(1)に挿入/削除(要素への参照がある場合に注意)

  • 中央のO(n)で取得

  • 最初または最後の要素O(1)を取得する

ベクトル:使用しないでください。これはArrayListに似た古い実装ですが、すべてのメソッドが同期されています。これは、マルチスレッド環境での共有リストの正しいアプローチではありません。

HashMap

O(1)のキーによる挿入/削除/取得

TreeSetの 挿入/削除/ O(log N)への包含

O(1)のHashSet挿入/削除/包含/サイズ


1

実際には、メモリの局所性は、実際の処理においてパフォーマンスに大きな影響を与えます。

「ビッグデータ」処理とランダムアクセスでのディスクストリーミングの使用の増加は、これを中心にアプリケーションを構造化すると、大規模なパフォーマンスを劇的に改善できることを示しています。

配列に順次アクセスする方法がある場合、それは断然最高のパフォーマンスです。パフォーマンスが重要な場合は、これを目標として設計することを少なくとも検討する必要があります。


0

うーん、Arraylistは次のような場合に使用できます。

  1. 存在する要素の数がわからない
  2. しかし、インデックスを使用してすべての要素にランダムにアクセスする必要があります

たとえば、連絡先リストのすべての要素(サイズは不明)をインポートしてアクセスする必要があります。


0

配列の基数ソートおよび多項式演算にリンクリストを使用します。


0

1)上記で説明したように、挿入および削除操作は、ArrayList(O(n))と比較してLinkedListで良好なパフォーマンス(O(1))を提供します。したがって、アプリケーションで頻繁に追加と削除を行う必要がある場合は、LinkedListが最適です。

2)検索(getメソッド)操作はArraylist(O(1))では高速ですが、LinkedList(O(n))では高速ではないため、追加操作と削除操作が少なく、検索操作要件が多い場合は、ArrayListが最適です。


0

主な違いは、リストの一番上にあるものを頻繁に挿入または削除する必要があるかどうかだと思います。

配列の場合、リストの先頭から何かを削除すると、配列要素のすべてのインデックスをシフトする必要があるため、複雑度はo(n)になります。

リンクリストでは、ノードを作成し、ヘッドを再割り当てし、次への参照を前のヘッドとして割り当てるだけなので、これはo(1)です。

リストの最後に頻繁に挿入または削除する場合、複雑さがo(1)になるため配列が推奨されます。インデックスの再作成は必要ありませんが、リンクリストの場合は先頭から移動する必要があるため、o(n)になります。最後のノードに。

おそらくバイナリ検索を使用するため、リンクリストと配列の両方での検索はo(log n)になると思います。


0

ベンチマークを行ったところ、ランダム挿入の場合、リストクラスはLinkedListよりも実際に高速であることがわかりました。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 20000;
            Random rand = new Random(12345);

            Stopwatch watch = Stopwatch.StartNew();
            LinkedList<int> ll = new LinkedList<int>();
            ll.AddLast(0);
            for (int i = 1; i < count; i++)
            {
                ll.AddBefore(ll.Find(rand.Next(i)),i);

            }
            Console.WriteLine("LinkedList/Random Add: {0}ms", watch.ElapsedMilliseconds);

            watch = Stopwatch.StartNew();
            List<int> list = new List<int>();
            list.Add(0);
            for (int i = 1; i < count; i++)
            {
                list.Insert(list.IndexOf(rand.Next(i)), i);

            }
            Console.WriteLine("List/Random Add: {0}ms", watch.ElapsedMilliseconds);

            Console.ReadLine();
        }
    }
}

リンクリストには900ミリ秒、リストクラスには100ミリ秒かかります。

後続の整数のリストを作成します。新しい整数はそれぞれ、既にリストにある乱数の後に挿入されます。たぶん、Listクラスは単なる配列よりも優れたものを使用しています。


リストはクラスではなくインターフェースです
borgmater

0

配列は、断然、最も広く使用されているデータ構造です。ただし、リンクされたリストは、配列が扱いにくい、または控えめに言っても高価な独自の方法で有用であることがわかります。

リンクリストは、サイズが変化する状況でスタックとキューを実装するのに役立ちます。リンクリストの各ノードは、大多数のノードに影響を与えることなく、プッシュまたはポップできます。中央のどこかにノードを挿入/削除する場合も同様です。ただし、配列ではすべての要素をシフトする必要があるため、実行時間の点でコストがかかります。

バイナリツリーとバイナリ検索ツリー、ハッシュテーブル、および試行は、少なくともCでは、それらを構築するための基本的な要素としてリンクリストが必要なデータ構造の一部です。

ただし、リンクリストは、インデックスによって任意の要素を呼び出すことができると予想される状況では避けてください。


0

質問への簡単な答えはこれらのポイントを使用して与えることができます:

  1. 配列は、類似したタイプのデータ要素のコレクションが必要な場合に使用されます。一方、リンクリストは、ノードと呼ばれる混合型データリンク要素のコレクションです。

  2. 配列では、O(1)時間で任意の要素にアクセスできます。一方、リンクリストでは、リンクリスト全体を先頭から必要なノードまでO(n)時間でトラバースする必要があります。

  3. 配列の場合、特定のサイズを最初に宣言する必要があります。ただし、リンクリストのサイズは動的です。

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