選択
ソートは、
ソートアルゴリズムの一つで、
配列から最小値(または最大値)を探し出し、それを
配列の先頭要素と交換していくことで並べ替える手法です。このプロセスを未
ソート部分がなくなるまで繰り返すことで、最終的に
配列全体が
ソートされます。
選択
ソートの基本的な手順は以下の通りです。
1.
配列の先頭から未
ソート部分の中で最小値を持つ要素を探します。
2. 見つかった最小値の要素と未
ソート部分の先頭要素を交換します。
3. 未
ソート部分の範囲を一つ後ろにずらし、手順1と2を繰り返します。
4. 未
ソート部分がなくなるまで、またはすべての要素が
ソートされるまでこの手順を続けます。
昇順に
ソートする場合は最小値を、降順に
ソートする場合は最大値を探索します。また、各ループで最小値と最大値の両方を探し、それぞれを
配列の両端に配置する手法も存在し、これはdouble selection sortと呼ばれます。
擬似コード例
以下に選択
ソートの擬似コードを示します。
for i = 0 to n-2
min_index = i
for j = i+1 to n-1
if array[j] < array[min_index]
min_index = j
swap array[i] and array[min_index]
動作例
初期データとして `8 4 3 7 6 5 2 1` を昇順に
ソートする例を以下に示します。太字は
ソート済みの部分を表します。
1 4 3 7 6 5 2 8 (1回目のループ終了時)
1 2 3 7 6 5 4 8 (2回目のループ終了時)
1 2 3 7 6 5 4 8 (3回目のループ終了時)
1 2 3 4 6 5 7 8 (4回目のループ終了時)
1 2 3 4 5 6 7 8 (5回目のループ終了時)
1 2 3 4 5 6 7 8 (6回目のループ終了時)
1 2 3 4 5 6 7 8 (7回目のループ終了時)
この例では、
ソートが進むにつれて
配列が整理されていく様子がわかります。しかし、
アルゴリズムは
配列全体を完全に処理する必要があります。
計算時間
選択
ソートの計算量は、要素の比較回数と要素の交換回数で評価できます。
要素の比較: 配列の要素数を`n`とすると、各ループで `n-1`, `n-2`, ... , `1` 回の比較が行われます。したがって、比較回数の合計は `n(n-1)/2` となります。
要素の交換: 各ループで最大1回の交換が行われるため、最大で `n-1` 回の交換が行われます。
バブル
ソートと比較すると、選択
ソートは比較回数は同じですが、交換回数が少ないため、一般的に高速です。
安定性
選択
ソートは安定な
ソートアルゴリズムではありません。安定
ソートとは、同じ値を持つ要素の相対的な順序が
ソート後も保持される性質を指します。選択
ソートの場合、同値の要素の順序が入れ替わる可能性があるため、安定ではありません。
例えば、`2a 2b 1`という
配列を
ソートする場合、選択
ソートでは、
`2a 2b 1` (初期状態)
`1 2b 2a` (1回目のループ終了時)
`1 2b 2a` (2回目のループ終了時)
`1 2b 2a` (終了状態)
となり、`2a`と`2b`の順序が入れ替わっています。したがって、選択
ソートは安定ではないことがわかります。
実装例
以下に、
Python、C#、
Schemeでの選択
ソートの実装例を示します。
python
def selection_sort(arr):
n = len(arr)
for i in range(n-1):
min_idx = i
for j in range(i+1, n):
if arr[j] < arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr
C#
csharp
public static void SelectionSort(int[] arr)
{
int n = arr.Length;
for (int i = 0; i < n - 1; i++)
{
int minIdx = i;
for (int j = i + 1; j < n; j++)
{
if (arr[j] < arr[minIdx])
{
minIdx = j;
}
}
(arr[i], arr[minIdx]) = (arr[minIdx], arr[i]);
}
}
scheme
(define (selection-sort lst)
(let ((len (length lst)))
(letrec ((sort-loop (lambda (i)
(if (< i (- len 1))
(let ((min-idx (letrec ((find-min (lambda (j current-min index)
(if (= j len)
(cons current-min index)
(if (< (list-ref lst j) current-min)
(find-min (+ j 1) (list-ref lst j) j)
(find-min (+ j 1) current-min index)))))
(find-min (+ i 1) (list-ref lst i) i))))
(min-val (car min-idx))
(min-index (cdr min-idx)))
(if (not (= i min-index))
(let ((temp (list-ref lst i)))
(set! lst (list-update lst i (list-ref lst min-index)))
(set! lst (list-update lst min-index temp)))))
(sort-loop (+ i 1))))))
(sort-loop 0))
lst))
(define (list-update lst index value) (if (= index 0) (cons value (cdr lst)) (cons (car lst) (list-update (cdr lst) (- index 1) value))))
まとめ
選択
ソートは、実装が比較的簡単で理解しやすい
アルゴリズムですが、時間計算量が大きいため、大規模なデータには向いていません。しかし、空間計算量が限られている状況や、
ソートするデータ量が小さい場合には有効な選択肢となることがあります。より効率的な
ソートアルゴリズムが必要な場合は、
クイックソートやマージ
ソートなどを検討する必要があります。