単方向リストは、要素の1つ1つが「値」と「次の要素」を持ち、先頭の要素から「次の要素」を順にたどって読む並びです。要素を途中に入れるときも、途中から外すときも、要素は動かしません。「次の要素」を書き換えて、つなぎ先を替えるだけです。
期間限定クーポンPR|Udemy の講座科目B オリジナル模試 3回分・60問42%OFF 1,500円第2部 定番処理の型 全26回A10-2リストの挿入と削除一覧を開く
A1-1トレース表の書き方A1-2繰返しのトレースA1-3配列のトレースA2-1配列を全部見るA2-2最大・最小を見つけるA2-3探す(線形探索)A2-4入れる・消すA3-12次元配列の走査A3-22次元配列の集計A4-1文字列を1文字ずつA4-2文字列照合A5-1交換して並べるA5-2挿入ソートA6分割して並べるA7-12分探索A7-2ハッシュ表探索A8-1再帰とはA8-2再帰のトレースA9スタックとキューA10-1リストを読むA10-2リストの挿入と削除A11木構造A12木の巡回A13グラフA14AI・データを題材にしたプログラムA15コントロールブレーク処理
この回の内容を動画で見る
次のページで講義の目次と無料の講義を見られます。受講ボタンとクーポンコードもそこにあります。
この回の内容を動画で見る
解くための再現性が身につく講義29本小テスト86問模試3回分IT資格の森で見る
講義「リストのつなぎ替え」(全6分44秒)は、IT資格の森(月額1,280円)の会員向けです。科目Bは、1行ずつ値を追うアニメの講義と、講義ごとの小テスト、本番と同じ形の模擬試験がそろっています。
つなぎ替え ── 書き換えるのは next だけ
科目Bでは、リストの要素をクラス ListElement で表すことがあります。メンバ変数は2つで、val が値、next が次の要素です。次の要素が無い末尾の要素では、next は未定義です。変数 listHead が、先頭の要素を指しています(たどり方は A10-1)。
A → B → D とつながったリストと、まだつながっていない C
上の段=要素の値(val) / 下の段=その要素の next が指している要素 / 緑の矢印=next が指している先 / 網掛け=未定義 / 下の箱=変数が指している要素
val
A
B
C
D
next
B
D
未定義
未定義
listHeadA の要素
prevB の要素
nodeC の要素
先頭からたどると A → B → D。C の要素は、どの next からも指されていないので、まだリストに入っていません。
図の A・B・C・D は val の値で、ここでは要素の呼び名にも使います。prev と node は、つなぎ替えに使う変数です。いまは prev が B の要素を、node が C の要素を指しています。
リストの順番は next だけで決まっています。だから、next を書き換えれば順番が変わります。配列の途中に入れるときのように、うしろの要素を1つずつずらす必要はありません。
つなぎ替えの行では、← の左に「どれかの要素の next」が来ます。たとえば次の行です。
prev.next ← node
prev.next は、prev が指している要素の next です。上の図なら prev は B の要素を指しているので、B の next のこと。この行は、B の next を、node が指している要素(C)へ書き換えます。prev と node が指す先は変わりません。ただし、この1行だけでは D を指す next が無くなってしまいます。正しい手順は、次の節の2行です。
挿入 ── 2行。新しい要素の next を先に書く
prev が指している要素のうしろに、node が指している要素を入れる手続です。
○sounyuu(ListElement: prev, ListElement: node) node.next ← prev.next prev.next ← node
当サイトで作った例です。
B と D のあいだに C を入れるので、prev に B の要素、node に C の要素を渡して呼び出します。手続の中の prev は、渡された B の要素そのものを指しているので、prev.next を書き換えると、リストの中の B の next が変わります。
B と D のあいだに C を入れる ── next を2か所、この順に書き換える
上の段=要素の値(val) / 下の段=その要素の next が指している要素 / 緑の矢印=next が指している先 / 網掛け=未定義 / 下の箱=変数が指している要素 / 琥珀の枡=その行で読んだ next / 緑の枡=その行で書き換えた next
① node.next ← prev.next C の next に、B の next の中身を写す
val
A
B
C
D
next
B
D
D
未定義
listHeadA の要素
prevB の要素
nodeC の要素
prev.next(B の next)は D を指しています。同じ D を、C の next にも入れました。これで C のうしろが D につながりました。先頭からたどると、まだ A → B → D です。
② prev.next ← node B の next を、C へ向ける
val
A
B
C
D
next
B
C
D
未定義
listHeadA の要素
prevB の要素
nodeC の要素
B の next が D から C に変わりました。先頭からたどると A → B → C → D。
どの要素も動いていません。書き換えたのは、C の next と B の next の2か所だけです。
呼び出したあと、先頭からたどると A → B → C → D になります。
行の順番 ── 逆にすると、うしろが切れる
2行の順番を入れ替えると、同じ呼び出しでも結果が変わります。
○sounyuu(ListElement: prev, ListElement: node) prev.next ← node node.next ← prev.next
当サイトで作った、誤った順の例です。
2行を逆にすると、D へたどり着けなくなる
上の段=要素の値(val) / 下の段=その要素の next が指している要素 / 緑の矢印=next が指している先 / 網掛け=未定義 / 下の箱=変数が指している要素 / 琥珀の枡=その行で読んだ next / 赤=誤った書き換え / 灰=先頭からたどり着けなくなった要素
① prev.next ← node 先に、B の next を C へ向けた
val
A
B
C
D
next
B
C
未定義
未定義
listHeadA の要素
prevB の要素
nodeC の要素
B の next は D を指していましたが、C で上書きしました。D の要素は残っていますが、D を指している next が、もうどこにもありません。先頭からたどると A → B → C で止まります。
② node.next ← prev.next C の next に、B の next の中身を写した
val
A
B
C
D
next
B
C
C
未定義
listHeadA の要素
prevB の要素
nodeC の要素
prev.next は、①で C に変わっています。だから C の next に入るのは C 自身です。先頭からたどると A → B → C → C → C … と、C から先へ進めません。
D の要素は残っていますが、先頭からたどり着く道がありません。
うしろの要素 D を指していたのは、B の next だけでした。B の next を書き換える前に、その中身を新しい要素の next へ写しておく。これが、正しい順で1行目に node.next ← prev.next を置く理由です。
削除 ── 1行。前の要素の next を、1つ先へ向ける
prev が指している要素の次の要素を、リストから外す手続です。
○sakujo(ListElement: prev) prev.next ← prev.next.next
当サイトで作った例です。
さきほど C を入れた A → B → C → D から、B を外します。prev に渡すのは、外す要素そのものではなく、その1つ前の要素です。B の1つ前は A なので、A の要素を渡して呼び出します。.next が2つ続くところは、左から1つずつ読みます。
A → B → C → D から B を外す ── A の next を、1つ先の C へ向ける
上の段=要素の値(val) / 下の段=その要素の next が指している要素 / 緑の矢印=next が指している先 / 網掛け=未定義 / 下の箱=変数が指している要素 / 琥珀の枡=その行で読んだ next / 緑の枡=その行で書き換えた next / 灰=先頭からたどり着けなくなった要素
実行する前 prev.next.next が指しているのは、どの要素か
val
A
B
C
D
next
B
C
D
未定義
listHeadA の要素
prevA の要素
prev.next は A の next なので B。prev.next.next は、その B の next なので C です。
prev.next ← prev.next.next のあと
val
A
B
C
D
next
C
C
D
未定義
listHeadA の要素
prevA の要素
A の next が B から C に変わりました。先頭からたどると A → C → D。B の要素も、B の next も、そのまま残っています。
B を指している next が無くなったので、先頭からたどっても B には着きません。これが「外れた」状態です。
呼び出したあと、先頭からたどると A → C → D になります。削除は、外す要素を飛び越すように、1つ前の要素の next を1つ先へ向けます。外す要素そのものを書き換える行はありません。
先頭の要素には、1つ前の要素がありません。先頭を指しているのは listHead なので、listHead を1つ先へ向けます。実際の出題でも、先頭を外す行はこう書かれています。
(基本情報技術者試験 科目B サンプル問題 問10)
listHead ← listHead.next
先頭に入れるときも、書き換える相手が listHead になります。次の2行を、この順で書きます。
node.next ← listHead
listHead ← node
先に listHead の中身(いまの先頭)を node.next へ写してから、listHead を node へ向けます。prev のうしろに入れるときと同じ順番です。
次に読む
| A11 | 木構造 ─ 根・葉・親子・深さ |
| A10-1 | リストを読む ─ next をたどって、未定義で止まる |
| サンプル問題 問10 | 単方向リストから、指定された位置の要素を削除する問題の解説 |
| G12 | オブジェクト ─ 変数.メンバ変数 の読み方 |
| 目次 | 基本情報技術者試験 科目B 攻略ガイド |
この記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では、引用した箇所の文言を改変していません(省略や補いがある場合は、その場に明記しています)。
本文の例題は当サイトのオリジナルです。


