なぜ他のデータ構造の代わりに配列を使用するのですか?


195

私がプログラミングをしているとき、配列が他の形式よりも情報を格納するのに適している例を見たことがありません。私は確かに、プログラミング言語の追加された「機能」がこれを改善し、それによってそれらを置き換えたことを理解していました。それらが置き換えられるのではなく、いわば新しい命が与えられるようになりました。

では、基本的に、配列を使用する意味は何ですか?

これがコンピュータの観点から配列を使用する理由ではなく、プログラミングの観点から配列を使用する理由です(微妙な違い)。コンピュータがアレイをどう処理するかは問題ではありませんでした。


2
コンピュータがアレイで何をするかを考えてみませんか?STRAIGHTストリートがあるため、ハウスナンバリングシステムがあります。配列についても同様です。
lcn 2013

他のデータ構造」または「別の形式」とはどういう意味ですか?そして、何のために?
tevemadar

回答:


771

レッスンに戻る時間です。これらのことについては、今日の派手なマネージ言語ではあまり考えていませんが、同じ基盤の上に構築されているので、Cでメモリがどのように管理されるかを見てみましょう。

始める前に、「ポインタ」という用語の意味を簡単に説明します。ポインタは、メモリ内の場所を「指す」単なる変数です。このメモリ領域の実際の値は含まれていません。メモリアドレスが含まれています。メモリのブロックをメールボックスと考えてください。ポインタは、そのメールボックスへのアドレスになります。

Cでは、配列は単なるオフセット付きのポインタであり、オフセットはメモリ内でどれだけの距離を探すかを指定します。これはO(1)アクセス時間を提供します。

  MyArray   [5]
     ^       ^
  Pointer  Offset

他のすべてのデータ構造は、これに基づいて構築されるか、隣接するメモリをストレージに使用しないため、ランダムアクセスのルックアップ時間が不十分になります(シーケンシャルメモリを使用しないことには他の利点もあります)。

たとえば、6つの数値(6、4、2、3、1、5)を含む配列があるとします。メモリ内では、次のようになります。

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================

配列では、メモリ内で各要素が隣接していることがわかります。C配列(MyArrayここで呼び出されます)は、最初の要素へのポインタです。

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^
MyArray

検索したい場合はMyArray[4]、内部的に次のようにアクセスします。

   0     1     2     3     4 
=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
                           ^
MyArray + 4 ---------------/
(Pointer + Offset)

ポインターにオフセットを追加することにより、配列内の任意の要素に直接アクセスできるため、配列のサイズに関係なく、同じ時間内に任意の要素を検索できます。つまり、取得MyArray[1000]にはと同じ時間がかかりMyArray[5]ます。

別のデータ構造はリンクリストです。これはポインタの線形リストで、それぞれ次のノードを指します

========    ========    ========    ========    ========
| Data |    | Data |    | Data |    | Data |    | Data |
|      | -> |      | -> |      | -> |      | -> |      | 
|  P1  |    |  P2  |    |  P3  |    |  P4  |    |  P5  |        
========    ========    ========    ========    ========

P(X) stands for Pointer to next node.

各「ノード」を独自のブロックにしたことに注意してください。これは、それらがメモリ内で隣接していることが保証されていない(そしておそらくそうなっていない)ためです。

P3にアクセスする場合、メモリのどこにあるのかわからないため、直接アクセスすることはできません。ルート(P1)がどこにあるかがわかっているので、代わりにP1から開始して、目的のノードへの各ポインターを追跡する必要があります。

これはO(N)ルックアップ時間です(ルックアップコストは各要素が追加されると増加します)。P4に到達するよりもP1000に到達する方がはるかに高価です。

ハッシュテーブル、スタック、キューなどの上位レベルのデータ構造はすべて内部で配列(または複数の配列)を使用できますが、リンクリストとバイナリツリーは通常ノードとポインタを使用します。

配列を使用するだけでなく、値を検索するために線形トラバーサルを必要とするデータ構造を誰もが使用するのに不思議に思うかもしれませんが、それらには用途があります。

もう一度アレイを取ってください。今回は、値「5」を保持する配列要素を見つけたいと思います。

=====================================
|  6  |  4  |  2  |  3  |  1  |  5  |
=====================================
   ^     ^     ^     ^     ^   FOUND!

この状況では、それを見つけるためにポインターに追加するオフセットがわからないので、0から始めて、それが見つかるまで上に移動する必要があります。つまり、6つのチェックを実行する必要があります。

このため、配列内の値の検索はO(N)と見なされます。配列が大きくなると、検索のコストが増加します。

非シーケンシャルデータ構造を使用すると利点が得られる場合があると私が言った上記を覚えていますか?データの検索はこれらの利点の1つであり、最良の例の1つはバイナリツリーです。

バイナリツリーは、リンクリストに似たデータ構造ですが、単一のノードにリンクする代わりに、各ノードは2つの子ノードにリンクできます。

         ==========
         |  Root  |         
         ==========
        /          \ 
  =========       =========
  | Child |       | Child |
  =========       =========
                  /       \
            =========    =========
            | Child |    | Child |
            =========    =========

 Assume that each connector is really a Pointer

データがバイナリツリーに挿入されると、いくつかのルールを使用して、新しいノードを配置する場所を決定します。基本的な概念は、新しい値が親よりも大きい場合は左に挿入し、小さい場合は右に挿入するというものです。

つまり、バイナリツリーの値は次のようになります。

         ==========
         |   100  |         
         ==========
        /          \ 
  =========       =========
  |  200  |       |   50  |
  =========       =========
                  /       \
            =========    =========
            |   75  |    |   25  |
            =========    =========

バイナリツリーで75の値を検索する場合、次の構造のため、3つのノード(O(log N))にアクセスするだけで済みます。

  • 75は100未満ですか?右のノードを見る
  • 75は50より大きいですか?左のノードを見る
  • 75があります!

ツリーにはノードが5つありますが、残りの2つを調べる必要はありませんでした。ノード(およびその子)には、探している値を含めることができないためです。これにより、最悪の場合はすべてのノードにアクセスする必要があることを意味する検索時間が得られますが、最良の場合には、ノードのごく一部にアクセスするだけで済みます。

これは、配列がビートになる場所であり、O(1)アクセス時間にもかかわらず、線形O(N)検索時間を提供します。

これはメモリ内のデータ構造に関する非常に高レベルの概要であり、多くの詳細をスキップしていますが、うまくいけば、他のデータ構造と比較した配列の長所と短所を示しています。


1
@ジョナサン:5番目の要素を指すように図を更新しましたが、MyArray [4]もMyArray [5]に変更したため、依然として正しくありません。インデックスを4に戻し、図をそのままにしておけば、問題ありません。 。
Robert Gamble

54
これは、「コミュニティウィキ」についてこのバグが私にバグを与えるものであり、この投稿は「適切な」担当者の価値があります
Quibblesome

8
素敵な答え。しかし、あなたが説明するツリーは、二分探索木です-二分木は、すべてのノードが最大で2つの子を持つツリーです。要素を任意の順序で含むバイナリツリーを作成できます。あなたが説明するように二分探索木は編成されています。
gnud

1
良い説明ですが、私はひっくり返ることはできません...バイナリサーチツリーに項目を並べ替えることが許可されている場合、配列内の要素を並べ替えてバイナリサーチも機能しないようにできないのはなぜですか?ツリーのO(n)挿入/削除についてはさらに詳しく説明しますが、配列のO(n)について説明します。
市場

2
データセットのサイズに関連してアクセス時間が対数的に増加するため、バイナリツリー表現はO(log n)ではありませんか?
エヴァンプライス

73

O(1)のランダムアクセスの場合、これを無効にすることはできません。


6
どの点で?O(1)とは何ですか?ランダムアクセスとは何ですか?なぜそれを打つことができないのですか?別のポイント?
jason

3
O(1)は一定の時間を意味します。たとえば、配列のn-esim要素を取得する場合、リンクされたリストを使用して、そのインデクサー(array [n-1])を介して直接アクセスするだけです。たとえば、頭を見つけて、次のノードに順次n-1回進みます。これはO(n)の線形時間です。
CMS

8
Big-O表記は、アルゴリズムの速度が入力のサイズに基づいてどのように変化するかを示します。O(n)アルゴリズムは、2倍の数の項目で実行するには2倍の時間がかかり、8倍の数の項目で実行するには8倍の時間がかかります。つまり、O(n)アルゴリズムの速度は[cont ...]によって異なります
Gareth

8
入力のサイズ。O(1)は、入力のサイズ( 'n')がアルゴリズムの速度に影響しないことを意味します。これは、入力サイズに関係なく一定の速度です
Gareth

9
私はあなたのO(1)を見て、あなたをO(0)にします。
Chris Conway、

23

すべてのプログラムが同じことをしたり、同じハードウェアで実行したりするわけではありません。

これは通常、さまざまな言語機能が存在する理由です。配列は、コンピューターサイエンスの中心的な概念です。配列をリスト/行列/ベクトル/高度なデータ構造で置き換えると、パフォーマンスに重大な影響を与え、多くのシステムではまったく実行不可能になります。問題のプログラムのために、これらの「高度な」データ収集オブジェクトの1つを使用する必要がある場合はいくつもあります。

私たちのほとんどが行うビジネスプログラミングでは、比較的強力なハードウェアをターゲットにできます。これらの構造では開発者が目標をより速く達成できるため、C#でのリストまたはJavaでのベクトルを使用するのが適切な選択であり、これにより、このタイプのソフトウェアをより特色にすることができます。

組み込みソフトウェアやオペレーティングシステムを作成する場合は、多くの場合、アレイの方が適しています。配列は機能が少なくなりますが、RAMの使用量が少なくなり、コンパイラーはコードをより効率的に最適化して配列を検索できます。

私はこれらの場合の多くの利点を除外していると確信していますが、要点を理解していただければ幸いです。


4
皮肉なことに、Javaでは、VectorではなくArrayList(またはLinkedList)を使用する必要があります。これは、通常は不要なオーバーヘッドである、同期されているベクトルとの関係です。
アシャーリー2009年

0

配列の利点を確認する方法は、配列のO(1)アクセス機能が必要であり、したがって大文字である場所を確認することです。

  1. アプリケーションのルックアップテーブル(特定のカテゴリ応答にアクセスするための静的配列)

  2. メモ化(複雑な関数の結果が既に計算されているため、関数値を再度計算しないように、log xなど)

  3. 画像処理を必要とする高速コンピュータビジョンアプリケーション(https://en.wikipedia.org/wiki/Lookup_table#Lookup_tables_in_image_processing

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