コンテンツへスキップ

データ型とデータ構造

A-Levelコンピュータ科学 · トピック 10

查看幻灯片 練習する
このトピックのビデオレッスン 動画ページを開く
17:40

データ型と構造体

プログラムが格納するすべての値にはデータ型が必要であり、適切なものを選ぶことが重要です。ある商品が在庫にあるかどうかを格納する場合来说、yesという単語を書き込むこともできます…

英語ナレーション・英語+中文字幕 burning-in

10.1

データタイプの選択

シラバス
受験者は以下的能力を備えているべきである: メモおよびガイドライン
問題解決に適した データ型 を選択して使用する 整数, 実数, char, 文字列, ブール, 日付 を含む(擬似コードでは以下のデータ型を使用する:INTEGER, REAL, CHAR, STRING, BOOLEAN, DATE, ARRAY, FILE)
単一の識別子の下で異なるデータ型のデータセットを保持するための レコード構造 の目的を理解する レコード構造を定義する擬似コードを書く
レコード構造からデータを読み書きし、レコード構造にデータを保存する擬似コードを書く

出典: Cambridge International シラバス

すべての変数にはデータタイプが必要です — 保持する値の種類と許容される操作です:

  • INTEGER — 整数(42, -7)。カウント、インデックス、ID用。
  • REAL — 小数部を持つ数(3.14)。金銭、測定値用。
  • STRING — 引用符内の文字("Hello")。テキスト用。
  • CHAR — 単一の文字('A')。
  • BOOLEAN — TRUEまたはFALSE。フラグ用。
  • DATE — カレンダー日付。

正確に収まる最も小さいタイプを選ぶ:整数のカウントにはINTEGER、フラグにはBOOLEAN(文字列"yes"/"no"ではなく)を使う。

"適切なデータタイプを与えよ」という表は、値の使用法によって決定されます:クラス平均得点はREAL(小数部があるため);メールアドレスはSTRING;生徒数はINTEGER;生徒が支払い済みかどうかはBOOLEAN;生年月日はDATE;配列のインデックスは常にINTEGER;単一のグレード文字はCHAR;電話番号はSTRINGである。なぜなら、0で始まり、計算には使われないからである。BOOLEANは、検索がターゲットを見つけたかどうか、メンバーが支払い済みのかどうか、座席が予約されているかどうかなど、2つの状態しかないフラグに使用される。識別子表については、変数名も意味のあるものにしなければならない:NumberOfPeople、nではない。

10.1

レコード

レコード(レコード構造)は、一つの名前の下に複数の異なる種類のフィールドを保持する — 複数の値が一つのものを記述する場合に有用である。

TYPE TStockItem
    DECLARE ItemID : INTEGER
    DECLARE Category : STRING
    DECLARE ItemCost : REAL
    DECLARE InStock : BOOLEAN
ENDTYPE

これは型TStockItemを定義しており、それ可以使用する変数を宣言する:

DECLARE Item1 : TStockItem
DECLARE Items : ARRAY[1:100] OF TStockItem

ドット表記を使用して各フィールドにアクセスする:

Item1.Category ← "Fruit"
OUTPUT Item1.Category, " costs ", Item1.ItemCost

値が常にセットで存在する場合(顧客、在庫商品など)にレコードを使用し、無関係な値には個別の変数を使用します。

計算例。 クラブは、各生徒について、生徒ID(文字列)、氏名、生年月日、最大3つのクラブ番号(整数)を格納します。レコード型、$3000$人の生徒を保持する配列、および先頭要素に氏名を格納する文を宣言する疑似コードを書きなさい。

TYPE Student
    DECLARE StudentID : STRING
    DECLARE Name : STRING
    DECLARE DateOfBirth : DATE
    DECLARE Club : ARRAY[1:3] OF INTEGER
ENDTYPE

DECLARE Membership : ARRAY[1:3000] OF Student
Membership[1].Name ← "Li Wei"

得点:TYPEと識別子、ENDTYPE;各フィールドは適切な型で宣言され、配列は境界値とOF Studentで宣言され、フィールドはインデックスとドットで参照されます。「レコード宣言の誤りを述べよ」という問題は通常、欠落したENDTYPE、型のないフィールド、または算術値を保持すべきSTRINGとして宣言されたフィールドを指します。独自に得点される2つの慣例があります。一つは未使用の要素を現実的なデータになり得ない値(空の文字列、-1、0のID)でマークすることです。また、すべてのモジュールが未使用スロットを認識できるように、どこでも同じマーカーを使用するのが良い習慣であり、未使用のクラブフィールドは0となります。「3つの利点を述べよ」という問題に対して、レコードの配列を用いる利点は、一つのエンティティに関する全てのデータを一つの識別子の下に保持できる点、フィールドが異なるデータ型を持つことができる点、複数の平行配列を同期して管理する必要がなく一つの配列で代替できる点、全てのデータを一つのループで処理したり一つのパラメータとして渡したりできる点、そしてフィールドを追加する場合に型定義を変更すればよい点です。一人の顧客に対しては適した構造はレコード(一つの名の下の異なる型のフィールド)であり、全ての顧客に対してはレコードの配列です。

A TStockItem record drawn as a stack of four fields under one name — ItemID (INTEGER), Category (STRING), ItemCost (REAL), InStock (BOOLEAN) — reached with dot notation like Item1.Category
一つの名の下で異なる型の複数のフィールドを保持するのがレコード
探索

レコードは複数のフィールドを一つの名称の下にまとめます

レコードは関連するフィールドを束ねます。各フィールドは名前付きラベルであり、数値インデックスではなく、ドット表記(Item1.Category)でアクセスします。

English 日本語
array/əˈreɪ/ 配列
record/ˈrekɔːd/ レコード
record structure/ˈrekɔːd ˈstrʌktʃə/ レコード構造
field/fiːld/ フィールド
element/ˈelɪmənt/ 要素
bounds/baʊndz/ 境界
stack/stæk/ スタック
dimension/daɪˈmenʃn/ 次元
10.2

配列

シラバス
受験者は以下的能力を備えているべきである: メモおよびガイドライン
配列 に関連する技術用語を使用する インデックス, 上限, 下限 を含む
特定のタスクに適したデータ構造(1D または 2D 配列)を選択する
1D および 2D 配列の擬似コードを書く
配列データの処理を行う擬似コードを書く バブルソート によるソート、線形探索 による検索

出典: Cambridge International シラバス

配列とは、一つの名の下で同じ型の項目の順序立てられた集合であり、インデックスによって参照されます。

  • 要素 — 配列内の一つの項目。
  • 境界 — 有効なインデックスの最小値と最大値。
  • 次元 — 1-D(リスト)、2-D(表)など。
  • 下限と上限 — 最初の有効インデックスと最後の有効インデックス;要素数は上限から下限を引いた値に1を加えたものとなり、2-D配列では二つの個数の積となります。

したがってThisArray[n] ← 42において、この配列は一次元であり、インデックスは変数n(INTEGER)であり、そのインデックスの要素には42が入ります。配列を宣言するには、境界値だけでなくデータ型も必要です。小数点を含む可能性がある$120$の値を宣言するために:DECLARE Data : ARRAY[1:120] OF REAL;2列×$150$行の文字列表:DECLARE Data : ARRAY[1:150, 1:2] OF STRING、これは$300$の要素を持ちます。個別の変数よりも配列を用いる利点について、「2点説明せよ」という問題では、三十個の変数名ではなく一つの識別子である点、インデックスをカウンタとしてループで要素を処理できる点、サイズの変更が可能である点、全てのデータを一つのパラメータとしてモジュールに渡せる点が挙げられます。配列はまた、選択文の連鎖を置き換えることもできます。DaysInMonth[Month]は直接答えを検索するため、十二個のIF節を必要とせず、短く、書きやすく、保守しやすいです。

1-D配列

DECLARE Names : ARRAY[1:5] OF STRING
Names[3] ← "Cara"
OUTPUT Names[3]

FORループを使ってすべての要素を処理します:

FOR i ← 1 TO 5
    OUTPUT Names[i]
NEXT i
A row of indexed cells named myList, with indices 0 to 8 and the lower bound (first index) and upper bound (last index) marked
インデックスと境界を持つ1-D配列(リスト)

2-D配列(2D配列)

DECLARE Grid : ARRAY[1:3, 1:4] OF INTEGER
Grid[2, 3] ← 99

最初のインデックスは行、次のインデックスは列を表します。すべてのセルを訪問するために嵌套ループを使用します。単一のシーケンスには1-D、自然な2つの次元(グリッド、行×列)には2-Dを使用します。

A 3 by 4 grid with row indices and column indices; the cell at row 2, column 3 is highlighted
行と列のインデックスを持つ2-D配列(表)

一般的な演算子

線形探索は、見つかるまで各要素をチェックします:

FOR i ← 1 TO n
    IF A[i] = Target THEN
        OUTPUT "Found at ", i
    ENDIF
NEXT i

合計、カウント、最大値、最小値を見つけるには、累加変数を初期化してから走査します:

Max ← A[1]
FOR i ← 2 TO n
    IF A[i] > Max THEN
        Max ← A[i]
    ENDIF
NEXT i

バブルソートは配列を整列させます:隣り合うペアを比較して順序が逆になっている場合は交換し、通し回すことを繰り返します。一連の通し回しが交換を行わないまで続けます。

Paper 2では、これらのアルゴリズムを疑似コードおよび言葉での手順として求められ、時にはその「効率的」な形式でも求められます:

  • 最大値: Largestを最初の要素に設定します。残りの各要素について、もしLargestより大きい場合、Largestに保存します。ループ後、Largestを出力します。最大値の位置については、Largestが変わるたびにインデックスを保存する第二の変数を維持します。
  • 位置を返す線形探索: ループ前にFoundAt ← -1を設定します(有効なインデックスになり得ない値なので、「見つかっていない」ことを意味します)。配列をループして、要素が一致したらインデックスを保存してループを抜け、ループ後にFoundAtをテストします。
  • 非空白要素のカウントまたは出力: 各要素を未使用要素のマーカー(""または-1)と比較し、異なるもののみをカウントまたは出力します。
  • 要素の削除: 線形探索でそのインデックスを見つけ、それ以降の要素をすべて始点到め方向へ一つずつ移動して隙間を埋めます。最後尾の要素を未使用としてマークするか(またはカウントを減らす)。
  • 整列済み配列への挿入: 要素が大きい最初のインデックスを見つけ、その要素とそれ以降の要素をすべて終点指向方向へ一つずつ移動し、隙間に新しい値を格納します。
  • 効率的なバブルソート: 一連の通し回しが交換を行わなければすぐに終了するためのSwappedフラグ、および最大値が既に終点に到達しているため通し回すごとに1つずつ減少する上限値。
REPEAT
    Swapped ← FALSE
    FOR Index ← 1 TO Limit - 1
        IF Data[Index] > Data[Index + 1] THEN
            Temp ← Data[Index]
            Data[Index] ← Data[Index + 1]
            Data[Index + 1] ← Temp
            Swapped ← TRUE
        ENDIF
    NEXT Index
    Limit ← Limit - 1
UNTIL Swapped = FALSE

得点は、スワップが行われなくなるまで繰り返す外側ループ、IF内で設定されるフラグ、一時変数を用いた3行の交换処理、および縮小する上限値に与えられます。「ステップ(段階的精密化)によるソート」とは:整列するまで繰り返す;各パスで隣接するペアを比較する;順序が逆なペアを交换する;各パス後、未整列部分の最大値が末尾に来る、というものです。レコードまたは平行データの2つの1次元配列は、1つのループと1つのインデックスで処理されます。2次元配列は、外側ループが行数、内側ループが列数を指す重複ループを必要とし、ある行内の探索では行インデックスが固定され、列方向にループします。

バブルソートの1パス (5, 2, 8, 1): 5と2を比較して交换し 2, 5, 8, 1 にする;5と8を比較(既に正しい順序);8と1を比較して交换し 2, 5, 1, 8 にする。これにより最大値である8が末尾に到達した
バブルソートの1パス: 隣接するペアを比較・交换し、最大値を末尾へ浮き上がらせる
探索

2次元配列

行と列を選択して特定の要素を読み取ります。データグリッドの格納方法とインデックス付けの仕組みです。

English 日本語
lower bound/ˈləʊə baʊnd/ 下界
upper bound/ˈʌpə baʊnd/ 上界
linear search/ˈlɪnɪə sɜːtʃ/ 線形探索
bubble sort/ˈbʌbl sɔːt/ バブルソート
レッスンを視聴
10.3

ファイル

シラバス
受験者は以下的能力を備えているべきである: メモおよびガイドライン
ファイル が必要となる理由を理解する
1つ以上の行で構成される テキストファイル を扱う擬似コードを書く

出典: Cambridge International シラバス

ファイルとは、セカンダリストレージに格納されたデータであり、プログラム実行の間に保持されます。RAM内の変数はプログラム終了時に消失するため、データを永続的に保存したい場合(高得点記録、レコード、設定など)、プログラムはファイルに書き込みます。ファイルはまた、プログラム間でデータを共有したり、保存した状態から再起動したりするための手段でもあります。

RAMの変数はプログラム終了時に消失しますが、ディスク上のファイルは実行の間に保持されるため、プログラムはそこから読み込んで書き込みます
RAMの変数はプログラム終了時に消失しますが、ディスク上のファイルは実行の間に残ります

A text file holds one or more lines of readable characters; programs read and write text files line by line. Open a file before use and close it after:

OPENFILE "data.txt" FOR READ      // or FOR WRITE, FOR APPEND
WHILE NOT EOF("data.txt") DO
    READFILE "data.txt", LineString
    OUTPUT LineString
ENDWHILE
CLOSEFILE "data.txt"

EOF は読み出し前にファイルの終端を検査します。書き出すには:

OPENFILE "log.txt" FOR WRITE
FOR i ← 1 TO 100
    WRITEFILE "log.txt", "Event " & i
NEXT i
CLOSEFILE "log.txt"

常にすべてのファイルを閉じる——否则、バッファ書き込みが失われたり、他のプログラムへのアクセスが阻害されたりする可能性があります。

ファイルを使う理由(2点):データがプログラム終了後も保持されるため、次の実行時に利用可能である;他のプログラムと共有できる;メモリに収まらない分のデータも保持できる、という点です。テキストファイルがプログラムによって順次処理できる特徴は、それが開始端から順に読み取られる行の系列であることです。3つのモード:READ で先頭からの読み取り;WRITE で新しいファイルを作成し、これは既存の内容をすべて削除するため、追加用には使えません;APPEND で既存ファイルの末尾に行を追加。読み出しの前に必ず EOF をテストし、複数のモジュールが使う場合でもファイルは1回だけ開く。

** worked example.** テキストファイルの最終3行を、順に出力する手続 LastLines(FileName : STRING) の擬似コードを書け。

PROCEDURE LastLines(BYVAL FileName : STRING)
    DECLARE LineX, LineY, LineZ : STRING
    LineX ← ""
    LineY ← ""
    LineZ ← ""
    OPENFILE FileName FOR READ
    WHILE NOT EOF(FileName) DO
        LineX ← LineY
        LineY ← LineZ
        READFILE FileName, LineZ
    ENDWHILE
    CLOSEFILE FileName
    OUTPUT LineX
    OUTPUT LineY
    OUTPUT LineZ
ENDPROCEDURE

新しい各行が入力されるたびに、前の3つの値が順にずれるため、ファイル終了時に3つの変数はその最後の3行を保持します。行数が少ないファイルでは空文字列が出力されます。最初 の5行を出力するには、読み取った行数をカウントし、ループを5または EOF のいずれか小さい方で停止します。空のファイルは、EOF が開いた直後に TRUE であることで検出されます。

行内のフィールド。 テキストファイルは文字列を保持するため、レコードはセパレーター文字で結合された1行として書き込まれ、各数値やブール値は NUM_TO_STR (そして STR_TO_NUM または "TRUE" との比較で読み戻し)を使って変換される。データに絶対に現れないセパレーターを選ぶ:名前の場合はコンマや | 、数値の場合はコンマ。名前にスペースが含まれる可能性がある場合、スペースは使わない。フィールドに任意の字符が含まれる可能性があれば、セパレーターがデータと混同されるおそれがあり、修正法は各フィールドを独立した行にするか、フィールドの長さをその前に書き込むことである。1行に1項目とするのは読み戻しが単純だが、より多くの行を使用し、レコードを単位として見にくくする。行が既知の順序(IDによる昇順)で並んだファイルを読む場合、より大きなIDを読み取った時点で検索を停止でき、末尾まで読む必要がない。ゲームを保存するたびに作成されるセーブファイルには、 player's name と日付時刻などの意味のあるファイル名が必要であり、これにより過去のセーブを復元できる。

テキストファイルの1行 (1023,Ali,12.50,TRUE) をコンマセパレーターで分割し、株品レコードの4つのフィールドにする。各フィールドに必要な変換:数値フィールドには STR_TO_NUM、文字列はそのまま、ブール値には TRUE との比較
テキストファイルの1行は1レコード:セパレーターで結合されたフィールドは、読み戻しのときにそれぞれの型に変換される
探索

ファイルの扱い:開く → 使用する → 閉じる

すべてのファイルが通すライフサイクルをステップごとに確認してください。忘れやすい2つのポイントは、ループ内で読み込む際にEOFテストを行うこと、そして最後に必ずファイルを閉じることです。

English 日本語
file/faɪl/ ファイル
secondary storage/ˈsekəndəri ˈstɔːrɪdʒ/ 補助記憶装置
text file/tekst faɪl/ テキストファイル
end of file/end ɒv faɪl/ ファイル終端
10.4

抽象データ型 (ADT)

シラバス
受験者は以下的能力を備えているべきである: メモおよびガイドライン
ADT がデータとそれらに対する操作の集合であることを理解する
スタック, キュー, リンクedList が ADT の例であることを理解する スタック, キュー, リンクedList の主要な特徴を説明し、特定の状況での使用を正当化する
スタック, キュー, リンクedList を用いてデータを格納する これらの構造に対して擬似コードを書く必要はないが、データ追加、編集、削除ができるべきである
キュー, スタック, リンクedList が 配列 を用いてどのように実装されるかを説明する

出典: Cambridge International シラバス

リンクドリスト:ポインタの再接続による挿入
スタックとキュー:LIFOとFIFO

抽象データ型 (ADT) とは、データコレクションとそれに対する操作の組み合わせであり、何をするかによって定義され、どのように格納されているかではありません。ユーザーは操作を通じてのみ動作し、実装は隠蔽されているため、ADTを使用するコードに影響を与えずに変更できます。スタック、キュー、リンクドリストの3つを知っておくべきです。

1点定義:ADT(抽象データ型)とは、データの集合と、そのデータに対する一連の操作の集合を指す。スタック、キュー、連結リスト、二進木、配列はすべてADTである。選択肢を正当化する: 項目が到着した順に処理される必要がある場合(印刷ジョブ、キー入力、店舗での顧客など)、FIFO(先着先出し)であるためキューを選ぶ;最も最近の項目を最初に処理する必要がある場合(元に戻す、ウェブページの履歴戻る、順序の逆転、ネスト呼び出しの戻りアドレスなど)、LIFO(後入先出し)であるためスタックを選ぶ;整列されたシーケンスの真ん中で挿入や削除が頻繁に行われる場合、ポインタのみが変わり、要素をシフトする必要がないため連結リストを選ぶ。スタックとキューを比較する: どちらも順序を持つ線形構造であり、配列とポインタで実装され、追加前に満杯チェック、削除前に空チェックが必要である。スタックは1つのポインタを持ち、両端が同一で追加・削除を行う。キューは2つのポインタを持ち、一方の端で追加し、もう一方の端で削除を行う。

スタック

スタックはLIFO(Last In, First Out)順に動作する。操作:push(上部への追加)、pop(上部からの削除)、peek(上部の参照)、および空/満杯のテスト。用途:Undo履歴、関数呼び出しの戻りアドレス、式解析、バックトラック。

配列に格納されたスタックの3つの状態を示す図;Push後にTopポインタが上昇し、Pop後に下降するが、スタックのベースは固定されている
PushとPopはトップポインタを変更し、ベースポインタは動かさない

計算例。 スタックには、下から 'P', 'N', 'Z', 'X', 'Y', 'W' が格納されており、スタックポインタは 'W'(200–207 のメモリ位置 202)にあります。操作 POP, POP, PUSH 'A', PUSH 'B', POP が実行されます。スタックには何があり、ポインタはどこを指していますか?

2回のpopで'W'、次に'Y'が削除され、pushでその代わりに'A'、次に'B'が追加されます。最後のpopで'B'が削除されます。現在、スタックには'P'、'N'、'Z'、'X'、'A'が含まれ、ポインタは'A'、位置203にあります。スタックに最も長く存在している値は最下部のアイテムである'P'です。スタックが空になるまで最大5回さらにpop可能ですが、空のスタックでのpopはエラーであり、そのためPop()ではまず空かどうかをチェックします。成功時にTRUEを返すPush()関数は、まずポインタが配列の先頭(満杯)にあるかチェックし、そうであればFALSEを返します。配列要素の使用前に初期化する必要はありません。ポインタのみがどの要素が使用中かを示すためです。

平らに積み重ねられた本の上積りの写真
本の上積りは目に見えるスタックである。本は上からしか加えたり取り出したりできないため、最後に置いたものが最初に取り出される=これがまさにLIFOである

キュー

キューはFIFO(First In, First Out)順に動作する。操作:enqueue(後部への追加)、dequeue(前方からの削除)、および空/満杯のテスト。用途:プリントスプーリング、スケジューリング、幅優先探索、バッファリング。

配列に格納された線形キューの3つの状態を示す図;EnqueueでRearポインタが進み、DequeueでFrontポインタが進むため、開始セルが空になり、使用不能となる
Enqueueは後部で追加し、Dequeueは前方から削除する

アイテムを追加する際の説明: キューが満杯でないことを確認し、末尾ポインタで指定された位置にアイテムを格納し、末尾ポインタ(およびカウント)をインクリメントする。削除時の説明: キューが空でないことを確認し、フロントポインタの位置にあるアイテムを読み出し、フロントポインタをインクリメント(カウントをデクリメント)する。使用する規約を明記せよ。末尾ポインタが次の空きスペースを示す場合、フロントと末尾ポインタが等しいことはキューが空であることを意味する。末尾ポインタが最終アイテムを示す場合、等しいポインタはアイテムが1つあることを意味する。線形キューではフロントポインタは常に前方に進むため、背後のセルは使用不能となる。これこそが以下の円形キューで解決される問題である。キューの2つの特徴を述べる:アイテムは後部で追加され前方から削除されるため、最初に追加されたものが最初に削除される。

壁に沿って遠くまで続く、1人ずつ並んだ長い行列の写真
人の行列は目に見えるキューである。後ろで並び、前から対応されるため、最も待っていた人が最初にサービスを受ける=これがまさにFIFOである

連結リスト

連結リストはノードの列としてデータを格納する。各ノードは値と次のノードへのポインタを保持する。ヘッドポインタは開始点を示し、最終ノードのポインタはSentinel(例:NULL)となる。操作:insert, delete, search, および traverse(順序通り各ノードを訪問する)。配列との違い:利点は安価な挿入・削除(ポインタ調整のみで済み)、欠点はランダムアクセスが遅い(ヘッドからポインタを辿る必要がある)。

値とnextポインタフィールドを持つノードが4つ並び、ヘッドポインタが最初のノードを指し、最終ノードのポインタはNULLである様子
連結リスト:各ノードが次のノードを指す

順序にノードを追加する(4点): ヘッドからリストをトラバースし、ポインタに従って位置の前のノード(値が小さい最後のノード)を見つける;フリーノードを取得して新しい値を格納する;新しいノードのポインタを前ノードが指していたアドレスに設定する;前ノードのポインタを新しいノードに設定する。新しい値が先頭に来る場合は、ヘッドポインタを変更する。ノードの削除: ノードの前のものを見つけ、そのノードのポインタを削除されたノードが指していたアドレスに設定して、リストをバイパスさせる;解放されたノードはフリーリストに戻る。1次元配列と比較して、リンクドリストでの挿入や削除には他のアイテムのシフトが不要であり、メモリが尽きるまでリストは拡大できる;コストは各アイテムに追加で保存されるポインタと、$n$番目のアイテムに到達するには直接インデックスがないため、$n$個のポインタをたどる必要があることである。

探索

リンクドリスト:ポインタで結合されたノード群

各ノードは値と次のノードへのポインタを保持します。挿入や削除は単にポインタの再接続を行うだけであり、配列のように要素がずれることはありません。

探索

スタックとキュー

プッシュとポップ。スタックはLIFO(後入れ先出し)、キューはFIFO(先入れ先出し)——これらは主要な抽象データ型です。

English 日本語
push/pʊʃ/ スタックへの格納
separator/ˈsepəreɪtə/ 区切り文字
linked list/lɪŋkt lɪst/ 連結リスト
queue/kjuː/ キュー
LIFO/ˈlaɪfəʊ/ 後入先出
FIFO/ˈfaɪfəʊ/ FIFO
pop/pɒp/ ポップ
enqueue/enˈkjuː/ エントゥ
dequeue/diːˈkjuː/ ディキュー
traverse/trəˈvɜːs/ トラバーサル
free list/friː lɪst/ フリーリスト
overflow/ˌəʊvəˈfləʊ/ オーバーフロー
underflow/ˌʌndəˈfləʊ/ アンダーフロー
circular array/ˈsɜːkjʊlə əˈreɪ/ 巡回配列
レッスンを視聴
10.4

配列を用いたADTの実装

配列によるスタック

Stack[1:MaxSize]にアイテムを保持し、整数Top(空の時は0)を使用する。

  • Push(x): Top = MaxSizeの場合スタックは満杯である(オーバーフロー);否则 Top ← Top + 1;Stack[Top] ← x。
  • Pop(): Top = 0の場合スタックは空である(アンダーフロー);否则 return Stack[Top] and Top ← Top - 1。

円形配列によるキュー

単純なキューではFrontとRearが端を過ぎて消えてしまい、先頭のスペースが無駄になる。解決策は円形配列であり、ポインタがMaxSizeに達すると1に戻り:

  • Enqueue(x): 満杯か確認;否则 Rear ← (Rear MOD MaxSize) + 1;Queue[Rear] ← x。
  • Dequeue(): 空か確認;否则 return Queue[Front] and Front ← (Front MOD MaxSize) + 1。

空と満杯を区別するために別のカウントを追跡する。

エンドポインタのアルゴリズムを言葉で表すと:カウントがサイズに等しい場合はキューが満杯であると報告して終了する;否则 エンドポインタに1を加える;これが最終インデックスを超えた場合は最初のインデックスに設定する;そこにアイテムを格納してカウントを1加算する。5点の「宣言と初期化を記述せよ」という問題の回答が示す宣言:サイズと要素型を持つ配列;最初インデックス(またはフロンツが最初インデックス、エンドが次の空きスペース)に初期化されたフロントポインタとエンドポインタ;そして、$0$に初期化されたアイテムのカウント。

例えば、MaxSize = 6の場合:Rear = 5であれば、(5 MOD 6) + 1 = 6となるので、次のアイテムはセル6に入れる;Rear = 6であれば、(6 MOD 6) + 1 = 1となり、ポインタはセル1へ戻る。

配列に格納された円形キュー;埋まったセルは最後のセルを超えて先頭へ wrap され、カーブした矢印がポインタが最終インデックスからセル1へ wrap している様子を示している
円形キューはポインタを配列の先頭へ wrap する

配列によるリンクドリスト

各がNextIndexインデックスを持つレコードの配列を使用する:

TYPE TNode
    DECLARE Value : INTEGER
    DECLARE NextIndex : INTEGER     // index of the next node, or -1 for end
ENDTYPE

DECLARE Nodes : ARRAY[1:MaxSize] OF TNode
DECLARE Head : INTEGER         // index of first node, -1 if empty
DECLARE FreeListHead : INTEGER // first available free node

フリーリストは未使用のスロットをチェーンしており、データリストが使用済みをチェーンしているのと同じようにする。挿入する場合:FreeListHeadからスロットを取り出し、新しいノードの値とNextIndexを設定し、前ノードのNextIndex(またはHead)を更新する。削除する場合:ノードを.unlinkし、そのスロットをフリーリストに戻す。これにより、配列の静的割り当てとリンク構造の柔軟性を併せ持てる。

配列による値と並列なNextIndex配列で実装された連結リスト; Headポインタが使用済みのノードを链接し、FreeListHeadポインタが空きスロットを链接しており、各ノードの末尾に NextIndex = -1 が付いています
配列に格納されたリンクドリスト:データ配列とポインタ配列

計算例。 リンクedListはData配列とPointer配列に格納されており、Startがインデックス1を指しています。リストは1 → 3 → 4です(インデックス1にはD40、インデックス3にはD32、インデックス4にはD11が含まれており、そのポインタは$\emptyset$です)。フリーリストはインデックス2から始まり、2 → 5 continuesします。D6をD32とD11の間に挿入します。

最初のフリーノード(インデックス2)を取り出し、FreeStartをそのポインタである5に設定する;D6をData[2]に格納する;Pointer[2]を保持している値Pointer[3](4)に設定する;Pointer[3]を2に設定する。リストは 1 → 3 → 2 → 4 となり、フリーリストは 5 → $\emptyset$ となる。「リンクドリストをどのように実装できるか」への答えはまさにこれらの部分である:データを格納するための配列(またはレコードの配列)、インデックスを保持するポインタのための並列配列、スタートポインタ、フリーリストポインタ、および終端のためのnull値(例:$-1$)。

Worked example. A circular queue is held in an array of size 5 (indices 0 to 4) with Front = 3, Rear = 3 and one item stored. Two items are added, then two are removed. Where are the pointers, and why use a circular queue at all? Every move uses (pointer + 1) MOD size, so the pointers wrap. Adding twice moves Rear: $3 \rightarrow 4$, then $4 \rightarrow 0$ (because $(4+1) \bmod 5 = 0$), so Rear = 0 and three items are stored. Removing twice moves Front the same way: $3 \rightarrow 4$, then $4 \rightarrow 0$, leaving Front = 0 and one item. The wrap is the whole point: in a linear array queue the pointers march to the end and the freed space at the front is wasted even when the queue is empty. Remember a queue removes at the Front and adds at the Rear - a stack uses one pointer for both.

探索

配列によるADTの実装

FIFO

キューは先入れ先出しであり、最後尾でエンキューし、先頭でデキューする。

English 日本語
index/ˈɪndeks/ 添字
pointer/ˈpɔɪntə/ ポインタ
node/nəʊd/ ノード
10.4

出題者が認める定義

定義問題は固定された用語に基づいて採点されます。これらを正確に覚えてください。

用語 定義
record 一つの識別子の下で異なるデータ型の複数のデータ項目(フィールド)を保持するデータ構造
array 一つの識別子の下で固定数の同じデータ型要素を保持し、それぞれインデックスによってアクセスされるデータ構造
index 配列の1つの要素を識別する数
upper bound, lower bound 配列の有効なインデックスの最大値と最小値
text file データを文字の行として格納し、プログラムが一度に1行ずつ読み書きするファイル
abstract data type データの集合とそのデータに対する一連の操作の組み合わせ
stack アイテムが同じ端(トップ)に追加され、同じ端から取り出されるリスト。最後に追加されたものが最初に取り出される(LIFO)
queue アイテムが後方(Rear)に追加され、前方(Front)から取り出されるリスト。最初に追加されたものが最初に取り出される(FIFO)
リンクリスト 各ノードがデータ項目と次のノードへのポインタを持ち、最初のノードへのスタートポインタを持つリスト
ポインタ ノードや構造体の位置のアドレス(またはインデックス)を保持する変数
線形検索 最初から順に各要素を確認し、ターゲットが見つかるか末尾に到達するまで行う
バブルソート 配列を繰り返し通過して隣り合うペアを比較し、順序不同れなものを交換し、一度も交換がないパスになるまで行う
English 日本語
data type/ˈdeɪtə taɪp/ データ型
Abstract Data Type/ˈæbstrækt ˈdeɪtə taɪp/ 抽象データ型
10.4

試験対策

  • 適切なデータ構造を選び、その理由を述べる(混合フィールドにはレコード、グリッドには2次元配列)。
  • スタック、キュー、リンクドリストを配列とポインタで実装する方法を知る(トップ; フロント/リアール; ネクスト)。
  • ADT(その振る舞い)と実装(配列とポインタ)を区別する。

一般的なミス

  • ENDTYPE やフィールドに型を持たないレコード宣言。すべてのフィールドは DECLARE 行で、型が指定されている。
  • ファイルの末尾を超えて読み込む、またはファイルの内容を維持すべき時にWRITEで書き込む。各読み込み前にEOF进行测试;追加するにはAPPENDを使用する。
  • 文字列として保持されるファイルに数字を文字列変換なしで書き込む。NUM_TO_STR出力、STR_TO_NUM戻す。
  • チェックを忘れる。Pushとenqueueではまず満杯テストを行い;Popとdequeueではまず空テストを行う。答えはその通りである。
  • ノードを挿入する際に残りのリストを失う。前のノードのポインタを変える前に、新しいノードのポインタを古いネクストノードに設定する。
  • 「見つかりません」と言わない直列探索。位置を$-1$で初期化し、ループ後にテストする。

このトピックのインタラクティブ授業

一歩ずつ進め、即時チェック付きの問題で学習します。

過去問

A-Levelコンピュータ科学 の他のトピック

ログインまたはアカウント作成

IGCSE, A-Level & AP