令和8年度の科目B、その4問目です。ここで「単方向リスト」という新しい仕組みが出てきます。名前は難しそうですが、やっていることは「配列を2本ならべて、片方に次の場所の番号を書いておく」だけです。この記事では、その2本の配列を指でたどれるようになるところから始めます。
出題
まず原文のまま読んでみてください。記号の読み方は次の節にまとめてありますので、読めなくてもここでは問題ありません。
出典:令和8年度 基本情報技術者試験 科目B 公開問題 問4
次のプログラム中の a と b に入れる正しい答えの組合せを,解答群の中から選べ。ここで,配列の要素番号は1 から始まる。
単方向リストを,配列dataList と配列pointerList の二つの配列で表現する。dataList にリストの要素の値を格納し,pointerList にリストの次の要素に対応するdataList の要素番号を格納する。単方向リストの先頭は,dataList[1] 及びpointerList[1] の組みである。単方向リストの末尾に対応する pointerList の要素は未定義である。dataList のうち単方向リストの要素の値を格納していない要素と,対応するpointerList の要素は未定義である。
プログラムが扱うdataList 及びpointerList の内容を図1 に示す。先頭の次の要素の要素番号は,pointerList[1] に格納された 3 であり,値は dataList[3] に格納された 20 である。その次の要素の要素番号はpointerList[3] に格納された 2 であり,値はdataList[2] に格納された 30 である。
dataList
pointerList
図1 dataList 及びpointerList の内容 (注記 網掛けはその要素が未定義であることを示す)
関数orderList は,図1 のdataList 及びpointerList で表現した単方向リストの値を,単方向リストの先頭からたどって順番に格納した配列を返す。関数 orderList が返す配列を図2 に示す。
図2
図2 関数orderList が返す配列
〔プログラム〕
大域: 整数型の配列: dataList ← {10, 30, 20, 40, 未定義の値}
大域: 整数型の配列: pointerList ← {3, 4, 2, 未定義の値, 未定義の値}
○整数型の配列: orderList()
整数型: i, p ← 1
整数型の配列: linearList ← {} // 要素数0の配列
for (i を 1 から dataListの要素数 まで 1 ずつ増やす)
linearListの末尾 に dataList[p]の値 を追加する
if (a が 未定義)
繰返し処理を終了する
endif
p ← b
endfor
return linearList
解答群
ア a:dataList[p] b:i
イ a:dataList[p] b:pointerList[p]
ウ a:pointerList[p] b:i
エ a:pointerList[p] b:pointerList[p]
答えだけ先に見る
正解は エ(a・b とも pointerList[p])です。なぜエになるのかは、この記事で2手に分けて絞ります。
この問題に出てくる記号
読めない記号があったときだけ開いてください。先に全部読む必要はありません。
擬似言語の記号 16コ の読み方をひらく
| 書き方 | 読み方 |
|---|---|
a ← b |
右の値を、左へ入れる。入れられた側(左)は前の中身が消えて上書きされます。読まれた側(右)はそのままです |
dataList[p]dataList[p]の値 |
配列 dataList の p 番目の枡。角かっこの中を添字といい、変数も書けます(p が 3 なら dataList[3])。「の値」は「その枡の中身」という念押しで、指しているものは同じです |
dataList[3] の 3 |
要素番号(=添字)。配列の何番目の枡かを表します。この記事ではふつうに「3番」と書きます。この試験では 1 から数えます(0 からではありません) |
整数型 |
その箱に何を入れるかの決まり。整数型 なら整数だけが入ります。整数型の配列 は「整数を入れる枡を並べたもの」です(1つの箱ではなく、枡が並んだもの) |
整数型: i, p ← 1 |
この行では箱を2つ用意します。← が付いているのは p のほうなので、1 が入るのは p です。i はこのあと for が値を入れるので、はじめの値は要りません |
return ◯ |
◯ を返して、その関数はそこで終わり。呼んだ側にその値が渡ります |
| a | この問題で答えを入れる場所(空欄)。中の a b はその名前で、値ではありません。dataList[p] の角かっことは別ものです |
大域: |
関数の外に置く、という印。どの関数からも見えます。だから orderList() は何も受け取らずに dataList を読めます |
○整数型の配列: orderList() |
ここから関数が1つ始まるという印。関数=ひとまとまりの処理につけた名前です。整数型の配列 は返すものの型、括弧が空なので受け取る値は無し |
{} |
要素が1つも入っていない配列。{10, 30} なら2つ入った配列です |
// … |
コメント。人が読むための書き込みで、実行されません。行の終わりまでがコメントです |
dataListの要素数 |
その配列に入っている個数。{10, 30, 20, 40, 未定義の値} なら 5 です(未定義の分も1つと数えます) |
〜の末尾 に 〜 を追加する |
配列を1つ伸ばして、いちばん後ろに置く。{10} の末尾に 20 を追加すると {10, 20} になります |
for (…)endfor |
あいだの行を何度もくり返す。i を 1 から 5 まで 1 ずつ増やす なら、i が 1, 2, 3, 4, 5 と変わりながら5回実行されます。くり返しの1回ぶんを、この記事では「1周」と呼びます |
if (…)endif |
かっこの中が成り立つときだけ、あいだの行を実行する。成り立たなければ endif の次へ飛びます |
◯ が 未定義 |
◯ に何も入っていないとき成り立つ。図では網掛けの枡です。未定義の枡を読んで、別の箱へ入れる行も書けてしまいます ── そのときは、入れた先が未定義になります(それまで何が入っていても上書きされます) |
繰返し処理を終了する |
for を途中で抜ける。回数が残っていても、ここで endfor の先へ出ます |
これで手が止まるところがあれば、下の教科書から、先にそこだけ読んでください(G4 のような番号は、当サイトの擬似言語の教科書の第何回かを表します)。
{10, 30} の書き方と要素番号の数え方G4-2配列の読み書き ─ 添字に変数を書くとどうなるかG5選択処理 if ─ 条件が成り立つとき、どこからどこまでが実行されるかG6繰返し処理 for ─ くり返しの回数と、途中で抜けること2つの配列で「つながり」を表す
ここがこの問題の背骨です。配列はふつう、並んでいる順に読みます。ところがこの問題では、読む順番を別の配列に書いてあるのです。
2本の配列の役割
dataList その場所の値
pointerList(=行き先の欄) 次に行く場所の番号(値ではありません)
同じ番号の2枡で1つの「場所」です。dataList[3] と pointerList[3] で「3番の場所」。
図1の2本を、番号をそろえて並べてみます。
dataList = その場所の値
pointerList = 次に行く場所の番号
網掛けのところが未定義です。何も入っていない、という意味です。
では、この2本をどう使うのか。問題文に「単方向リストの先頭は,dataList[1] 及びpointerList[1] の組み」とあるので、1番の場所から始めます。1つの場所ですることは2つあって、使う枡がそれぞれ違います。そこを分けて見ます。
1番の場所で、何をするか ── 値を手にするのと、次を知るのは別の枡
琥珀=いま読んでいる枡 / 灰=もう読んだ枡 / 矢印=次にどこへ行くか / 破線=矢印の先=次に来る場所 / 網掛け=未定義
手順1 いる場所の値は、同じ番号の dataList にある
dataList
110
230
320
440
5未定義
pointerList
13
24
32
4未定義
5未定義
いま 1番にいます。場所の番号と、値の入っている枡の番号は同じなので、手にする値は dataList[1] = 10。ここで pointerList は使いません。
手順2 次にどこへ行くかだけを、pointerList が持っている
dataList
110
230
320
440
5未定義
3 番へ
pointerList
13
24
32
4未定義
5未定義
pointerList[1] の中身は 3。これは値ではなく、次に行く場所の番号です。だから次は 3番。3番へ着いてから、また同じ番号の dataList[3] = 20 を手にします。
1つの場所ですることは、いつもこの2つです。①同じ番号の dataList から値を手にする。②同じ番号の pointerList を見て、次の行き先を知る。これを、行き先が無くなるまでくり返すだけ。値は同じ番号のところ。ちがう番号を指すのは、行き先だけ。
先頭から、指でたどってみる
さっきの2つを、行き先が無くなるまでくり返すだけです。そのとき、手にした値を順に別の配列へ書き写していきます ── この記事では、手にして書き写すところまでをまとめて「拾う」、書き写す先を linearList(問題文の名前)と呼びます。これが、最後に返す答えの配列になります。
まず1回、自分でやってみてください。
最後までたどると、こうなります。
先頭からたどる ── 行き先の欄に書かれた番号の枡へ、1歩ずつ移る
琥珀=いまいる枡 / 矢印=次にどこへ移るか / 破線=矢印の先=次に読む枡 / 緑=その歩で並べた値 / 網掛け=未定義 / ▲=いま見ている枡(網掛けの枡は、これで指します)
① 1番にいる ── 値 10 を拾う
dataList
110
230
320
440
5未定義
3 番へ
pointerList
13
24
32
4未定義
5未定義
linearList
110
値 10 は、いまいる場所と同じ番号の dataList[1] から。pointerList[1] の 3 は次の行き先で、矢印の先=次に来る場所です。
② 3番にいる ── 値 20 を拾う
dataList
110
230
320
440
5未定義
2 番へ
pointerList
13
24
32
4未定義
5未定義
linearList
110
220
pointerList[3] は 2。矢印は左へ向きます ── たどる順は、配列に並んでいる順とは関係がありません。
③ 2番にいる ── 値 30 を拾う
dataList
110
230
320
440
5未定義
4 番へ
pointerList
13
24
32
4未定義
5未定義
linearList
110
220
330
pointerList[2] は 4。枡の並びの上では3番目を飛び越して右へ移ります(3番の場所には、もう②で来ています)。
④ 4番にいる ── 値 40 を拾う
dataList
110
230
320
440
5未定義
pointerList
13
24
32
4未定義▲
5未定義
linearList
110
220
330
440
pointerList[4](▲ の枡)が網掛け=未定義。行き先が無いので矢印が出ません。ここで終わりです ── 4番がこのリストの末尾だった、ということです(「linearList の末尾に追加する」の末尾=配列のいちばん後ろとは別で、こちらは行き先が無い場所のことです)。
拾った値を順に並べると 10 → 20 → 30 → 40 ── これが図2の配列です。たどった場所は 1 → 3 → 2 → 4 と飛んでいるのに、拾った値は順に並びます。並び順を決めているのは配列の位置ではなく pointerList だ、ということです。5番の場所には一度も来ません ── どの行き先の欄にも 5 と書かれていないからです。
コードの中で、a は「止まる合図」、b は「次の番号」
いま指でやったことを、プログラムはどう書いているのか。注釈を付けて見ます。
大域: 整数型の配列: dataList ← {10, 30, 20, 40, 未定義の値}
値の入れ物。関数の外にあるので、どの関数からも見える
大域: 整数型の配列: pointerList ← {3, 4, 2, 未定義の値, 未定義の値}
次の番号の入れ物。こちらも関数の外
○整数型の配列: orderList()
受け取る値は無い(括弧の中が空)。大域の2つを直接読む
整数型: i, p ← 1
p の出発点は 1 ── 先頭が dataList[1] だから
整数型の配列: linearList ← {} // 要素数0の配列
答えを並べていく入れ物。はじめは空
for (i を 1 から dataListの要素数 まで 1 ずつ増やす)
上限は 5 回(dataList の枡の数)。ただし最後まで回るとは限らない ── 途中の if で抜けることがある
linearListの末尾 に dataList[p]の値 を追加する
値を拾う ── いまいる場所の値を、答えの末尾へ
if (a が 未定義)
空欄a ── ここで何を見て終わりと判断するか
繰返し処理を終了する
for の途中でも、ここで抜ける
endif
p ← b
空欄b ── 次の番号を、どこから取るか
endfor
return linearList
並べ終わった配列を返す
※ 右側の注釈は当サイトで書き加えたものです。プログラム自体は原文のまま引用しています。
p がこのプログラムの主役
p は「いま自分がいる場所の番号」。出発点は p ← 1 で、くり返しの中で p を書き換えることが「進む」ことにあたります。
空欄に入るものを決める
解答群を先に見ると、似た式が4つ並んでいて迷います。入れるべきものを自分で決めてから、最後に照合するほうが速いです。空欄は2つ。どちらもさっき指でやったことが、そのまま答えになります。
空欄 b ── p ← b は「次の場所へ移る」行
指でたどったとき、次の場所の番号はどこに書いてありましたか。1番にいるとき「次は3番」と教えてくれたのは pointerList[1] でした。読む欄の番号は、いまいる場所によって変わります ── そこが p です。決めるのは、この行です。
〔プログラム〕空欄b のある行
endif
p ← b
endfor
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
実際に p が書き換わるところを見ます。
同じ1行をくり返すと、p といっしょに読む欄がずれていく
琥珀=いまいる場所(p 番)の欄 / 矢印=pointerList の中身が、次に来る場所の番号になる / 灰=もう通った場所 / 網掛け=未定義
p = 1 のとき
dataList
110
230
320
440
5未定義
pointerList
13
24
32
4未定義
5未定義
中身の 3 が、新しい p
p = 3 のとき
dataList
110
230
320
440
5未定義
pointerList
13
24
32
4未定義
5未定義
中身の 2 が、新しい p
p = 2 のとき
dataList
110
230
320
440
5未定義
pointerList
13
24
32
4未定義
5未定義
3段とも、中身はまったく同じ2本の配列です。変わっているのは p ── p は「いまいる場所の番号」で、値をとる欄も(dataList[p])、次の行き先を読む欄も(pointerList[p])、どちらもその番号で決まります。プログラムに書いてある行は1つで、読む欄は p が決めています。
p が変われば、次に読む欄も同じだけずれます。1番にいれば pointerList[1]、3番にいれば pointerList[3] ── いつでも「いまいる番号の欄」です。
b に入るもの
p ← pointerList[p]
空欄 a ── if (a が 未定義) は「もう行き先が無いか」を見る行
決めるのは、この行です。
〔プログラム〕空欄a のある行(前後もいっしょに)
linearListの末尾 に dataList[p]の値 を追加する
if (a が 未定義)
繰返し処理を終了する
endif
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
たどり終えたのは 4番の場所でした。そこで空になっていたのは、どちらの枡だったか。
末尾の 4番の場所 ── 空なのは、どちらの枡か
dataList[4]
440
pointerList[4]
4未定義
値は入っている。行き先だけが空。
dataList[4] には 40 が入っています。空なのは pointerList[4] のほうです。止まる合図は「値が無い」ではなく「次の行き先が無い」。
a に入るもの
if (pointerList[p] が 未定義)
ここで初めて解答群を見る
a も b も pointerList[p]。解答群でこの組合せは エ ひとつだけです。
ちなみに i は for が数えている周の番号で、行き先の欄を一度も見ません。b に i を入れる ア・ウ は、そもそもリストをたどっていません。
本番の手順 ── 解答群を見る前に、空欄の行が何をするかを言う
- 空欄のある行が、何をする行かを言う。この問題なら「次へ移る行」と「終わりかを見る行」。
- 入るべきものを、自分の手でたどった結果から決める。次の番号がどこに書いてあったか、末尾で空だったのはどちらか ── どちらももう見ています。
- 決めてから解答群を見る。似た式が並んでいても、照合するだけになります。
答え合わせ ── 入れて動かすと図2が出る
空欄に エ を入れて、図1のデータで最後まで進めます。見るのは p(いまいる場所)と linearList(並べ終わったぶん)です。
1行ずつ追う 正解の エ(a・b とも pointerList[p])
出典:令和8年度 科目B 公開問題 問4(空欄に エ を入れたもの)
ループ開始前
2つの配列と、並べ終わったぶん
いま計算していること
変数の状態
return で返す配列
トレース表(進めると1行ずつ積み上がります)
正解は エ
a・b とも pointerList[p]。p は pointerList[p] で次の場所へ進み、その pointerList[p] が未定義になったところで止まります。進むのも止まるのも、同じ1本の配列が決めているということです。
空欄を埋めた orderList の全体を見る
○整数型の配列: orderList()
整数型: i, p ← 1
整数型の配列: linearList ← {} // 要素数0の配列
for (i を 1 から dataListの要素数 まで 1 ずつ増やす)
linearListの末尾 に dataList[p]の値 を追加する
if (pointerList[p] が 未定義)
繰返し処理を終了する
endif
p ← pointerList[p]
endfor
return linearList
同じ考え方で解く練習問題
身についたかどうかは、つなぎ方を変えた問題で確かめるのがいちばんです。プログラムは1文字も変えません。
当サイトで作った類題です
次のプログラムを、下の2本の配列ではじめから動かします(pointerList の ◯ 番目は ◯ 番の次に行く要素番号、先頭は要素番号1、網掛けは未定義、dataList の要素数は 5)。返ってくる配列はどれですか。
プログラムをもう一度見る(プログラムは「答え」の節とまったく同じ。変えたのは下の2本の配列だけです)
○整数型の配列: orderList()
整数型: i, p ← 1
整数型の配列: linearList ← {} // 要素数0の配列
for (i を 1 から dataListの要素数 まで 1 ずつ増やす)
linearListの末尾 に dataList[p]の値 を追加する
if (pointerList[p] が 未定義)
繰返し処理を終了する
endif
p ← pointerList[p]
endfor
return linearList
dataList
pointerList
「リストをたどる」問題を見分ける
この形は、配列が2本あって、片方に要素番号が入っているのが合図です。見つけたら、次の順で読みます。
| 見るところ | 読み取ること |
|---|---|
| どちらが「番号の配列」か | まず問題文を探す(この問題なら「pointerList にリストの次の要素に対応する dataList の要素番号を格納する」)。書いていなければ、中身が要素番号の範囲に収まっているほうが手がかり ── ただし値がたまたま小さい数のときは、これだけでは決められません |
| 出発点はどこか | 問題文か、p ← 1 のような出発点を決める代入のどちらかに書いてあります。この問題は両方にありました(「先頭は dataList[1]」+ p ← 1) |
| 進む代入はどれか | p ← 番号の配列[p] の形。同じ変数が添字にも左辺にも出るのが目印です |
| 止まる条件はどこを見ているか | 番号の配列を見ていれば正しい。値の配列を見ていたら、そこが誤りの候補です |
くり返しの上限が「配列の要素数」になっているのも、この形の目印です。ただしこれはどこかで未定義に行き着くつながり方を前提にしています ── もし pointerList が輪になっていれば同じ場所を何度でも通るので、上限まで回っても正しい答えにはなりません。
次に読む
この問題でどこに手間取ったかで、行き先が変わります。
dataList[p] が読めなかった人へG6繰返し処理 for ── 途中で抜けるとどうなるかが曖昧だった人へ一覧公開されている科目B 44問の解説 ── 同じ形の問題を続けて解きたい人へこの記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では問題文を改変せずに引用しています。
「pointerList を組み替えた練習問題」は当サイトのオリジナルです。

