第4部 三大馬鹿ソート
第4部 三大馬鹿ソート
このワークの目標
このワークでは、次の3つの基本的なソートを扱います。
- バブルソート
- 選択ソート
- 挿入ソート
授業では、仕組みが単純で、無駄な比較や交換も多いこの3つを「三大馬鹿ソート」と呼びます。
最初から疑似言語を暗記するのではなく、次の順番で進めます。
自分の手で並べ替える
↓
1回の比較や交換を言葉にする
↓
必要な位置や回数に名前を付ける
↓
IPA疑似言語で書く
このワークでは、配列の要素番号は 0から始まるものとします。
data[0] data[1] data[2] ...
2つの値の交換は、次のように書きます。
swap(data[a], data[b])
まずは、交換に使う一時変数の処理を省略し、どの2要素を交換するかに注目します。
1.3つのソートの違い
| ソート | 1回の基本動作 | 確定していく場所 |
|---|---|---|
| バブルソート | 隣り合う2つを比較して交換する | 大きい値から右側へ確定する |
| 選択ソート | 未確定部分の最小値を探し、左端と交換する | 小さい値から左側へ確定する |
| 挿入ソート | 次の値を左隣と比較し、交換しながら左へ入れる | 左側の整列済み範囲が広がる |
3つとも小さい順に並べられますが、「どの2つを比較するか」と「どこが確定するか」が違います。
2.バブルソート
2-1.まず手で動かしてみよう
バブルソートは、左端から隣り合う2つを比較します。
[6][2] 5 1 4 3
swap(6, 2)
2 6 5 1 4 3
左の値が右の値より大きいときだけ交換します。次は、一つ右に進んで 6 と 5 を比較します。

図1 バブルソートは隣り合う2つを比較する
右端まで比較すると1周です。1周すると、その時点で最も大きい値が右端へ移動します。
2-2.1周目をトレースしよう
次の数列について、比較する2つを [ ] で囲み、必要なら交換しよう。
7 3 6 1 5 2 4
比較1:[ ][ ] _ _ _ _ _
交換後:_ _ _ _ _ _ _
比較2:_ [ ][ ] _ _ _ _
交換後:_ _ _ _ _ _ _
比較3:_ _ [ ][ ] _ _ _
交換後:_ _ _ _ _ _ _
比較4:_ _ _ [ ][ ] _ _
交換後:_ _ _ _ _ _ _
比較5:_ _ _ _ [ ][ ] _
交換後:_ _ _ _ _ _ _
比較6:_ _ _ _ _ [ ][ ]
交換後:_ _ _ _ _ _ _
1周目の模範解答を見る
[7][3] 6 1 5 2 4
swap(7, 3)
3 7 6 1 5 2 4
3 [7][6] 1 5 2 4
swap(7, 6)
3 6 7 1 5 2 4
3 6 [7][1] 5 2 4
swap(7, 1)
3 6 1 7 5 2 4
3 6 1 [7][5] 2 4
swap(7, 5)
3 6 1 5 7 2 4
3 6 1 5 [7][2] 4
swap(7, 2)
3 6 1 5 2 7 4
3 6 1 5 2 [7][4]
swap(7, 4)
3 6 1 5 2 4 7
1周目が終わると、最大値の 7 が右端に移動します。
2-3.最後までトレースしよう
同じ数列について、各周が終わった時点の配列を書こう。
| 周 | 配列の状態 |
|---|---|
| 最初 | 7 3 6 1 5 2 4 |
| 1周目 | ________________ |
| 2周目 | ________________ |
| 3周目 | ________________ |
| 4周目 | ________________ |
| 5周目 | ________________ |
| 6周目 | ________________ |
最後までの模範解答を見る
交換が起きた場所を確認しよう。
最初 7 3 6 1 5 2 4
1周目 3 6 1 5 2 4 7
2周目 3 1 5 2 4 6 7
3周目 1 3 2 4 5 6 7
4周目 1 2 3 4 5 6 7
5周目 1 2 3 4 5 6 7
6周目 1 2 3 4 5 6 7
4周目で並び終わっていますが、単純なアルゴリズムでは5周目と6周目も実行します。この無駄は、後で改良できます。
2-4.日本語の穴を埋めよう
① 配列の左端から、右方向へ順番に調べる。
② 一度に比較するのは、隣り合った __ 個の値である。
③ 左の値が右の値より ____ とき、2つの値を交換する。
④ 1回の比較が終わったら、比較位置を右へ __ 個分進める。
⑤ 右端まで調べたら、1周終了する。
⑥ 1周だけでは並び終わらないので、同じ処理を何周も繰り返す。
日本語の模範解答を見る
② 2個
③ 大きいとき
④ 1個分
2-5.位置に名前を付けよう
1周の中で、左側の比較位置を i とします。
data[0] と data[1]
data[1] と data[2]
data[2] と data[3]
したがって、隣り合う2つは次のように表せます。
data[__] と data[____]
何周目かを表す変数は j とします。要素数を n とすると、この単純なバブルソートでは、j と i はどちらも0から n - 2 まで変化します。
2-6.疑似言語の穴を埋めよう
○bubbleSort(整数型の配列: data)
整数型: n ← dataの要素数
整数型: i, j
for (j を __ から ____ まで 1 ずつ増やす)
for (i を __ から ____ まで 1 ずつ増やす)
if (________ > ________)
swap(________, ________)
endif
endfor
endfor
バブルソートの完成形を見る
○bubbleSort(整数型の配列: data)
整数型: n ← dataの要素数
整数型: i, j
for (j を 0 から n - 2 まで 1 ずつ増やす)
for (i を 0 から n - 2 まで 1 ずつ増やす)
if (data[i] > data[i + 1])
swap(data[i], data[i + 1])
endif
endfor
endfor
3.選択ソート
3-1.まず手で動かしてみよう
選択ソートは、未確定部分の中から最小値を選び、未確定部分の左端と交換します。
[6] 2 5 [1] 4 3
swap(6, 1)
1 2 5 6 4 3

図2 選択ソートは左端と最小値を交換する
この交換で、左端の 1 の位置が確定します。次は、1 を除いた未確定部分から最小値を探します。
3-2.最後までトレースしよう
次の数列を選択ソートで並べ替えよう。
7 3 6 1 5 2 4
| 確定させる位置 | 未確定部分の最小値 | 最小値の位置 | 交換後の配列 |
|---|---|---|---|
| 0 | __ | __ | ______________ |
| 1 | __ | __ | ______________ |
| 2 | __ | __ | ______________ |
| 3 | __ | __ | ______________ |
| 4 | __ | __ | ______________ |
| 5 | __ | __ | ______________ |
選択ソートのトレースを見る
最初
7 3 6 1 5 2 4
位置0を確定:最小値1は位置3
[7] 3 6 [1] 5 2 4
swap(7, 1)
1 3 6 7 5 2 4
位置1を確定:最小値2は位置5
1 [3] 6 7 5 [2] 4
swap(3, 2)
1 2 6 7 5 3 4
位置2を確定:最小値3は位置5
1 2 [6] 7 5 [3] 4
swap(6, 3)
1 2 3 7 5 6 4
位置3を確定:最小値4は位置6
1 2 3 [7] 5 6 [4]
swap(7, 4)
1 2 3 4 5 6 7
位置4を確定:最小値5は位置4
交換なし
1 2 3 4 5 6 7
位置5を確定:最小値6は位置5
交換なし
1 2 3 4 5 6 7
3-3.日本語の穴を埋めよう
① 左側には、すでに位置が確定した値が並んでいる。
② まだ確定していない部分の中から、一番 ____ 値を探す。
③ 一番小さい値を見つけたら、値がある ____ を覚えておく。
④ 探し終わったら、最小値と未確定部分の一番 __ の値を交換する。
⑤ 交換後、確定済みの個数を __ 増やす。
日本語の模範解答を見る
② 小さい
③ 位置
④ 左
⑤ 1
3-4.必要な情報に名前を付けよう
| 表したいもの | 変数名 |
|---|---|
| 左から何個の位置が確定しているか | fixedCount |
| 一番小さい値があった位置 | minPos |
| 今調べている位置 | i |
| 配列の要素数 | n |
探し始める前は、未確定部分の左端を最小値候補にします。
minPos ← ______
その次の位置から、配列の最後まで調べます。
for (i を ________ から ______ まで 1 ずつ増やす)
3-5.疑似言語の穴を埋めよう
○selectionSort(整数型の配列: data)
整数型: n ← dataの要素数
整数型: fixedCount, i, minPos
fixedCount ← __
while (fixedCount < ______)
minPos ← ________
for (i を ________ から ______ まで 1 ずつ増やす)
if (________ < ________)
minPos ← __
endif
endfor
if (________ ≠ ________)
swap(________, ________)
endif
fixedCount ← fixedCount + __
endwhile
選択ソートの完成形を見る
○selectionSort(整数型の配列: data)
整数型: n ← dataの要素数
整数型: fixedCount, i, minPos
fixedCount ← 0
while (fixedCount < n - 1)
minPos ← fixedCount
for (i を fixedCount + 1 から n - 1 まで 1 ずつ増やす)
if (data[i] < data[minPos])
minPos ← i
endif
endfor
if (minPos ≠ fixedCount)
swap(data[fixedCount], data[minPos])
endif
fixedCount ← fixedCount + 1
endwhile
4.挿入ソート
4-1.まず手で動かしてみよう
挿入ソートでは、左側を整列済みの部分と考えます。次の値を左隣と比較し、左隣の方が大きければ交換します。
2 5 [6][1] 4 3
swap(6, 1)
2 5 1 6 4 3
2 [5][1] 6 4 3
swap(5, 1)
2 1 5 6 4 3
[2][1] 5 6 4 3
swap(2, 1)
1 2 5 6 4 3

図3 挿入ソートは左隣との交換を繰り返す
このワークでは、値を一時的に退避してシフトする方法ではなく、左隣との swap を繰り返す方法で考えます。
4-2.最後までトレースしよう
次の数列を挿入ソートで並べ替えよう。最初は、左端の 7 だけが整列済みです。
7 | 3 6 1 5 2 4
| 挿入する値 | 挿入後の配列 |
|---|---|
| 3 | ______________ |
| 6 | ______________ |
| 1 | ______________ |
| 5 | ______________ |
| 2 | ______________ |
| 4 | ______________ |
挿入ソートのトレースを見る
最初
7 | 3 6 1 5 2 4
3を挿入
[7][3] 6 1 5 2 4 → swap
3 7 | 6 1 5 2 4
6を挿入
3 [7][6] 1 5 2 4 → swap
3 6 7 | 1 5 2 4
1を挿入
3 6 [7][1] 5 2 4 → swap
3 [6][1] 7 5 2 4 → swap
[3][1] 6 7 5 2 4 → swap
1 3 6 7 | 5 2 4
5を挿入
1 3 6 [7][5] 2 4 → swap
1 3 [6][5] 7 2 4 → swap
1 3 5 6 7 | 2 4
2を挿入
1 3 5 6 [7][2] 4 → swap
1 3 5 [6][2] 7 4 → swap
1 3 [5][2] 6 7 4 → swap
1 [3][2] 5 6 7 4 → swap
1 2 3 5 6 7 | 4
4を挿入
1 2 3 5 6 [7][4] → swap
1 2 3 5 [6][4] 7 → swap
1 2 3 [5][4] 6 7 → swap
1 2 3 4 5 6 7
4-3.日本語の穴を埋めよう
① 左から整列済みの個数だけ、小さい順に並んでいるものとする。
② 次に処理するのは、整列済み部分のすぐ __ にある値である。
③ その値と、すぐ __ の値を比較する。
④ 左隣の値が今の値より ____ とき、2つを交換する。
⑤ 交換すると、今の値は配列中を __ 方向へ1つ移動する。
⑥ 左隣の値の方が大きい間、比較と交換を繰り返す。
⑦ 値を正しい位置まで入れたら、整列済みの個数を __ 増やす。
日本語の模範解答を見る
② 右
③ 左
④ 大きい
⑤ 左
⑦ 1
4-4.必要な情報に名前を付けよう
| 表したいもの | 変数名 |
|---|---|
| 左から何個が整列済みか | sortedCount |
| 今動かしている値の位置 | i |
| 配列の要素数 | n |
最初は data[0] の1個だけが整列済みなので、sortedCount は1です。
sortedCount ← 1
i ← sortedCount
交換を続ける条件は、次の2つです。
まだ左端まで来ていない
かつ
左隣の値の方が大きい
4-5.疑似言語の穴を埋めよう
○insertionSort(整数型の配列: data)
整数型: n ← dataの要素数
整数型: sortedCount, i
sortedCount ← __
while (sortedCount < __)
i ← ________
while ((__ > __) and (________ > ________))
swap(________, ________)
i ← ______
endwhile
sortedCount ← sortedCount + __
endwhile
挿入ソートの完成形を見る
○insertionSort(整数型の配列: data)
整数型: n ← dataの要素数
整数型: sortedCount, i
sortedCount ← 1
while (sortedCount < n)
i ← sortedCount
while ((i > 0) and (data[i - 1] > data[i]))
swap(data[i - 1], data[i])
i ← i - 1
endwhile
sortedCount ← sortedCount + 1
endwhile
5.3つのソートを見分けよう
練習1 説明から名前を答えよう
| 説明 | ソート名 |
|---|---|
| 隣り合う2つを比較し、左が大きければ交換する | ________ |
| 未確定部分の最小値を探し、未確定部分の左端と交換する | ________ |
| 次の値を、左隣との交換によって整列済み部分へ入れる | ________ |
練習1の模範解答を見る
| 説明 | ソート名 |
|---|---|
| 隣り合う2つを比較し、左が大きければ交換する | バブルソート |
| 未確定部分の最小値を探し、未確定部分の左端と交換する | 選択ソート |
| 次の値を、左隣との交換によって整列済み部分へ入れる | 挿入ソート |
練習2 比較する要素を答えよう
配列が次の状態のとき、次に比較する2要素または交換候補を答えよう。
(1)バブルソート
i が2のとき:data[__] と data[__]
(2)選択ソート
fixedCount が2、i が5のとき、比較する値:data[__] と data[____]
(3)挿入ソート
i が4のとき:data[____] と data[__]
練習2の模範解答を見る
(1)data[2] と data[3]
(2)data[5] と data[minPos]
(3)data[3] と data[4]
練習3 1周ごとの数列からソート名を当てよう
各ソートの一番外側の処理を1周とします。次の(1)〜(3)は、1周が終わるごとの数列を、整列が完了するまで並べたものです。数列の変化を見て、バブルソート・選択ソート・挿入ソートのどれかを答えよう。
(1)
6 2 5 1 4 3
↓
2 5 1 4 3 6
↓
2 1 4 3 5 6
↓
1 2 3 4 5 6
↓
1 2 3 4 5 6
↓
1 2 3 4 5 6
ソート名:____________
(2)
6 2 5 1 4 3
↓
1 2 5 6 4 3
↓
1 2 5 6 4 3
↓
1 2 3 6 4 5
↓
1 2 3 4 6 5
↓
1 2 3 4 5 6
ソート名:____________
(3)
6 2 5 1 4 3
↓
2 6 5 1 4 3
↓
2 5 6 1 4 3
↓
1 2 5 6 4 3
↓
1 2 4 5 6 3
↓
1 2 3 4 5 6
ソート名:____________
練習3の模範解答を見る
(1)バブルソート
外側の反復が終わるたびに、未確定部分の最大値が右端へ移動しています。
2 5 1 4 3 [6]
2 1 4 3 [5 6]
1 2 3 [4 5 6]
(2)選択ソート
未確定部分の最小値が、左側の確定位置へ一度に移動しています。離れた位置の値が交換されることも特徴です。
[1] 2 5 6 4 3
[1 2] 5 6 4 3
[1 2 3] 6 4 5
(3)挿入ソート
左側の整列済み範囲へ、次の値が1個ずつ挿入されています。
[2 6] 5 1 4 3
[2 5 6] 1 4 3
[1 2 5 6] 4 3
6.総合問題
問題1 処理からソートを特定しよう
次の処理は、どのソートを表しているか答えよう。また、空欄を埋めよう。
while (sortedCount < n)
i ← sortedCount
while ((i > 0) and (data[i - 1] > data[i]))
swap(data[____], data[__])
i ← i - 1
endwhile
sortedCount ← sortedCount + 1
endwhile
ソート名:____________
問題1の模範解答を見る
swap(data[i - 1], data[i])
挿入ソートです。
問題2 3つの処理を説明しよう
次の言葉を必ず使い、3つのソートの違いを日本語で説明しよう。
- 隣り合う
- 最小値
- 左隣
- 確定
- 整列済み
問題2の解答例を見る
バブルソートは、隣り合う2つを比較して交換し、大きい値から右側へ確定させます。
選択ソートは、未確定部分から最小値を探し、未確定部分の左端と交換して、小さい値から確定させます。
挿入ソートは、整列済み部分の右隣の値を、左隣と比較して交換しながら正しい位置へ入れます。
7.まとめ
- バブルソートは、隣り合う2つを比較して交換する
- 選択ソートは、未確定部分の最小値を探して左端と交換する
- 挿入ソートは、左隣との交換を繰り返して整列済み部分へ入れる
- 配列は0オリジンで考える
- 交換する2要素は
swap(data[a], data[b])と書く - 1回の比較、1周、アルゴリズム全体の順に組み立てる
- トレースでは、比較対象・交換・交換後の配列を毎回書く