配列とスタックの違いは何ですか?


10

ウィキペディアによると、スタック

後入れ先出し(LIFO)の抽象データ型と線形データ構造です。

一方で、配列

要素(値または変数)のコレクションで構成されるデータ構造であり、それぞれが少なくとも1つの配列インデックスまたはキーによって識別されます。

私が理解している限り、それらはかなり似ています。では、主な違いは何ですか?それらが同じでない場合、配列がスタックでできないこと、およびその逆ができることは何ですか?


5
あなたの質問は、あなたがウィキペディアで見つけたものでどのように答えられないのですか?配列の要素には任意の順序でアクセスできます。スタックはLIFOの順序でアクセスする必要があります。
カレブ

8
@Caleb何かを読んだからといって、コンセプトを理解しているわけではありません。私の考えでは、尋ねるまで完全に理解していませんでした。
ダイナミック

3
-1あなたは基本的にあなた自身の質問に答えを投稿しました。もう一度質問しているのは何ですか?
Andres F.

1
@AndresF。それが何を意味するのか理解できません...ウィキペディアの記事を見て、彼らが初めて言っていることを理解できれば、世界は完璧です。
ダイナミック

2
私はmeta.programmersを読んだだけで、なぜこの非質問をしたのかを理解しました。それはコンテストのためです。あなたがウィキペディアの記事を理解していなかったことに真剣に疑います。Shame on you:/
Andres F.

回答:


45

まあ、あなたは確かに配列でスタックを実装することができます。違いはアクセスです。配列には、要素のリストがあり、いつでも任意の要素にアクセスできます。(一連の木製のブロックがすべて並んでいると考えてください。)

しかし、スタックにはランダムアクセス操作はありません。とのみPushPeekありPop、スタックの一番上の要素のみを処理します。(木製のブロックが垂直に積み重ねられていると考えてください。タワーの上部の下には何も触れられないか、倒れる可能性があります。)


11
木製ブロック-素敵なアナロジー
ジェシーブラック

1
「スタックではランダムアクセス操作はありません」と言いますが、私は同意しません。私の回答にさらに詳細を追加します。
スコットウィットロック

スタックは間違いなく、ランダムアクセスで実装されている
old_timer

4
あなたはジェンガを吸う必要があります。
DisgruntledGoat

1
@メイソン、私は知っている。それは冗談だった。
DisgruntledGoat

6

純粋なスタックでは、唯一の許容操作はPushPop、およびPeek実用面で、それはまさに真実ではありません。むしろ、このPeek操作ではスタック上の任意の位置を確認できることがよくありますが、問題はスタックの一端を基準にしていることです。

したがって、他の人が言ったように、配列はランダムアクセスであり、すべてが配列の先頭を参照しています。

スタックでは、スタックの作業端でのみ追加/削除できますが、読み取りにはランダムアクセスがありますが、作業端で参照されます。それが根本的な違いです。

たとえば、スタック上のパラメーターを関数に渡す場合、呼び出し先はそれらを確認するためにパラメーターをポップする必要はありません。単にローカル変数をスタックにプッシュし、スタックポインターからのオフセットに基づいてすべてのローカル変数とパラメーターを参照します。配列だけを使用している場合、呼び出し先はどのようにしてそのパラメーターを探す場所を知るのでしょうか。呼び出し先が完了すると、呼び出し先はローカル変数をポップし、戻り値をプッシュし、呼び出し元に制御を返し、呼び出し元は戻り値(ある場合)をポップし、パラメータをスタックからポップします。美しさは、関数スタックにどれだけネストされていても機能することです(スタックスペースが不足しない場合)。

これは特定の使用/実装の1つですが、違いを示しています。配列は常に最初から参照されますが、スタックは常にいくつかの作業終了位置から参照されます。

スタックの可能な実装の1つは、配列に加えて、作業端がどこにあるかを覚えておくためのインデックスです。


私にはこれが答えです。配列とスタックの違いは何であるかという問題は、配列の制約が少なく、用途が広いように見えるのに、なぜスタックが必要なのかということです。そして、これはそれに答えます:スタックがより制約されているため、適切なケース(ここでは関数呼び出しなど)でスタックを使用すると、実装が論理的になります。ヘンコの「美」
トダンリー

4

ここで起こっている最大の混乱は、実装と基本的なデータ構造です。

ほとんどの(より基本的な言語の)配列は、任意の時点でアクセスできる固定長の要素のセットを表します。このような要素がたくさんあるという事実は、それがどのように使用されることになっているのかを何も知らない(そして、率直に言って、コンピュータは、使用法に違反しない限り、その使用方法を知りません/気にしません)。

スタックは、特定の方法で処理する必要があるデータを表すために使用される抽象化です。これは抽象的な概念です。なぜなら、上部に追加したり、上部から削除したりできるサブルーチン/メソッド/関数が必要であり、上部のデータは変更されないからです。この方法でアレイを使用するという純粋な選択。

多くの異なる種類のデータ構造からスタックを作成できます。配列(最大サイズあり)、動的配列(スペースがなくなると大きくなる可能性があります)、リンクリストなどです。個人的には、リンクされたリストはスタックの制限を最もよく表していると思います。最初の要素以外のものを見るには少し労力を費やさなければならず、前面に追加したり前面から削除したりするのは非常に簡単です。

したがって、配列を使用してスタックを作成できますが、それらは同等ではありません


3

彼らの責任は異なります:

  • スタックは要素をスタックにポップしてスタックから要素をプッシュできる必要があります。そのため、通常はメソッドPop()Push()

  • 配列の責任は、指定されたインデックスで要素を取得/設定することです


3

A \ arrayの任意のインデックスからアイテムを取得できます。

スタックでは、別のスタックBを使用して、スタックAの中央にあるアイテムを取得できます。

一番上のアイテムをAから取り出し、Aの目的のアイテムになるまでBに入れてから、BのアイテムをスタックAの一番上に戻します。

したがって、任意のインデックスを取得する機能を必要とするデータの場合、スタックの操作はより困難になります。

「後入れ先出し」動作が必要な状況では、スタックの方が配列よりオーバーヘッドが少なくなります。


0

それらは「非常に似ている」とまでは言いません。

配列は、後で順次またはインデックスを介してアクセスされるものを保持するために使用されます。データ構造は、あらゆる種類のアクセス方法(FIFO、LIFO、FILOなど)を意味するものではありませんが、必要に応じてその方法で使用できます。

スタックは、生成されたものを追跡する方法です。作成されるスタックのタイプに応じて、アクセス方法が暗黙的または必須になります。フレームスタックはLIFOの例です。免責事項-私はここでデータ構造分類法を混合している可能性があり、スタックは本当にLIFOのみを許可する可能性があります。それ以外の場合は、別のタイプのキューになります。

そのため、配列をスタックとして使用することはできます(ただし、使用したくはありません)が、スタックを配列として使用することはできません(実際に一生懸命努力しない限り)。


1
「オブジェクトのコレクションを格納するための構造」の領域では、それらが非常に類似しているとは言えません。プログラミングの概念の領域では、それらはかなり似ています。物事の領域では一般的に、それらはほとんど同じです。
カークブロードハースト

0

配列内のデータには、キーまたはインデックスでアクセスできます。スタックのデータは、スタックの最上部からポップされたときにアクセスできます。これは、配列のインデックスがわかっていれば、配列からのデータへのアクセスが簡単であることを意味します。スタックからデータにアクセスするということは、探している要素が見つかるまで要素をポップし続ける必要があるということです。つまり、データアクセスに時間がかかる可能性があります。


0

配列は、プログラマーの観点からあり、場所とサイズが固定されており、どこにいて、全体がどこにあるかがわかります。すべてにアクセスできます。

スタックを使用すると、スタックの一方の端に座っても、そのサイズや安全にどこまで移動できるかがわかりません。それへのアクセスは、割り当てた量に制限されます。ヒープまたはプログラム空間にたどり着いただけの場合、必要な量を割り当てるときに、そのことさえわからないことがよくあります。スタックのビューは、自分で割り当てた小さな配列で、必要なサイズであり、制御して確認できます。部分は配列と同じです。違いは、引数と用語のために、配列が別の配列の一方の端に貼り付けられていることです。これは、可視性がなく、どの程度大きくても小さくても、害を及ぼすことなく触れることができません。配列は、グローバルでない限り、とにかくスタックに実装されることが多いため、その関数の実行中、配列とスタックは同じスペースを共有します。

ハードウェア側に行きたい場合、それはもちろんプロセッサー固有ですが、一般に配列は既知の開始点/アドレスに基づいており、サイズはコンパイラー/プログラマーに知られており、アドレスはそれに計算されます、時々レジスターオフセットアドレッシングを使用する(このベースレジスター値とこのオフセットレジスター値で定義されたアドレスから値をロードします。同様に、コンパイル時に、必ずしもレジスターベースではない即時オフセットである可能性があります。もちろんプロセッサーに依存します)。高レベルのコードで配列にアクセスするのに似ています。スタックと同様に、利用可能な場合は、レジスターまたは即時オフセットアドレス指定を使用できますが、スタックポインター自体、またはそのためのスタックフレームへのアクセスに使用するためにコンパイラー/プログラマーによって予約されたレジスターのいずれかの特殊レジスターを使用することがよくあります関数。また、一部のプロセッサでは、スタックにアクセスするために特別なスタックアクセス関数が使用されます。あなたはプッシュとポップの指示を持っていますが、あなたが考えるほど頻繁には使用されておらず、この質問には実際には適用されません。一部のプロセッサでは、pushとpopは、スタック上のスタックポインタだけでなく、どこのレジスタでも使用できる命令のエイリアスであり、pushとpopがこの質問に関係しないようにしています。

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