ソートアルゴリズムとは?それぞれの種類別に具体例を交えて解説!

アルゴリズム
スポンサーリンク

この記事では

  • ソートアルゴリズム(Sort Algorithms)とは?
  • 代表的なソートアルゴリズムについて
  • 簡単な具体例とPythonの具体例
  • 状況別おすすめのソート

を紹介します。

主要なほかのアルゴリズムについて知りたい方はこちら

スポンサーリンク

ソートアルゴリズム(Sort Algorithms)とは

ソートアルゴリズム(Sort Algorithms)は、データを特定の順序(通常は昇順または降順)に並べ替えるための手法です。以下、いくつか代表的なソートアルゴリズムについて詳しく解説します。

1. バブルソート(Bubble Sort)

  • 概要: 配列の隣接する要素を比較して入れ替え、最も大きい(または最も小さい)要素を配列の末尾に移動させる手法。
  • 利点: 実装が簡単。
  • 欠点: 効率が悪く、大規模なデータセットには向いていない。

現実の例にすると

本棚に並んだマンガを順番通りの巻で並べ替えるとき、隣り合う本を比較して、巻の順番が逆なら交換する。これを繰り返して、順番通りの巻になる。

といった並べ替えの方法です。

バブルソートの具体的な例

: 配列 [5, 2, 9, 1, 5, 6] を昇順にソートする。

  1. 5 と 2 を比較し、2 の方が小さいので交換: [2, 5, 9, 1, 5, 6]
  2. 5 と 9 を比較し、交換なし: [2, 5, 9, 1, 5, 6]
  3. 9 と 1 を比較し、1 の方が小さいので交換: [2, 5, 1, 9, 5, 6]
  4. この過程を繰り返し、最終的にソートされる。

Pythonの例

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

# 使用例
arr = [64, 34, 25, 12, 22, 11, 90]
print("Sorted array:", bubble_sort(arr))

2. 選択ソート(Selection Sort)

  • 概要: 未ソート部分から最小(または最大)要素を選び、それをソート済み部分の末尾に追加する手法。
  • 利点: 実装が簡単で、メモリ効率が良い。
  • 欠点: 他のソートアルゴリズムと比較して効率が悪い。

現実の例にすると

クローゼットの中から一番小さいサイズの服を選び、それを一番左に置く。次に、残りの中からまた一番小さいサイズの服を選び、それを次に左に置く。

といった並べ替えの方法です。

選択ソートの具体的な例

: 配列 [29, 10, 14, 37, 14] を昇順にソートする。

  1. 最小値を見つけ、29 と 10 を交換: [10, 29, 14, 37, 14]
  2. 次に最小値の 14 を見つけ、29 と交換: [10, 14, 29, 37, 14]
  3. この過程を繰り返し、最終的にソートされる。

Pythonの例

def selection_sort(arr):
    n = len(arr)
    for i in range(n):
        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

# 使用例
arr = [64, 25, 12, 22, 11]
print("Sorted array:", selection_sort(arr))

3. 挿入ソート(Insertion Sort)

  • 概要: 配列の未ソート部分から要素を取り出し、ソート済み部分の適切な位置に挿入する手法。
  • 利点: 少量のデータに対して効率的。
  • 欠点: 大規模なデータセットには不向き。

現実の例にすると

トランプのカードを手札に加えるとき、既に並んでいるカードの適切な位置に挿入する。新しいカードを引くたびに、手札の中で正しい位置に挿入する。

といった並べ替えの方法です。

挿入ソートの具体的な例

: 配列 [12, 11, 13, 5, 6] を昇順にソートする。

  1. 11 を選び、12 の前に挿入: [11, 12, 13, 5, 6]
  2. 13 をそのまま維持: [11, 12, 13, 5, 6]
  3. 5 を選び、適切な位置に挿入: [5, 11, 12, 13, 6]
  4. この過程を繰り返し、最終的にソートされる。

Pythonの例

def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i-1
        while j >= 0 and key < arr[j]:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
    return arr

# 使用例
arr = [12, 11, 13, 5, 6]
print("Sorted array:", insertion_sort(arr))

4. マージソート(Merge Sort)

  • 概要: 配列を再帰的に半分に分割し、それぞれをソートした後に結合する手法。
  • 利点: 安定しており、大規模なデータに対しても効率的。
  • 欠点: メモリ使用量が多い。

現実の例にすると

大量の書類を整理するとき、まず半分に分け、それぞれをさらに半分に分けて整理し、最後にそれらを集めて整頓する。小さな部分から順に整理していこう。

といった並べ替えの方法です。

 マージソートの具体的な例

: 配列 [38, 27, 43, 3, 9, 82, 10] を昇順にソートする。

  1. 配列を半分に分割: [38, 27, 43, 3] と [9, 82, 10]
  2. 再帰的に分割し、小さい配列をソート: [3, 27, 38, 43] と [9, 10, 82]
  3. ソートされた配列を結合: [3, 9, 10, 27, 38, 43, 82]

Pythonの例

    if len(arr) > 1:
        mid = len(arr) // 2
        L = arr[:mid]
        R = arr[mid:]

        merge_sort(L)
        merge_sort(R)

        i = j = k = 0

        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i += 1
            else:
                arr[k] = R[j]
                j += 1
            k += 1

        while i < len(L):
            arr[k] = L[i]
            i += 1
            k += 1

        while j < len(R):
            R[j]
            j += 1
            k += 1
    return arr

# 使用例
arr = [12, 11, 13, 5, 6, 7]
print("Sorted array:", merge_sort(arr))

5. クイックソート(Quick Sort)

  • 概要: 基準となる要素(ピボット)を選び、それを基準に配列を分割して再帰的にソートする手法。
  • 利点: 一般に非常に高速。
  • 欠点: 最悪の場合、効率が低下する。

現実の例にすると

引っ越しの荷物を整理するとき、まず「重要なもの」と「重要でないもの」に分け、それぞれをさらに細かく分類していく。最初に基準を決めて、それに従って分けていく。

といった並べ替えの方法です。

クイックソートの具体的な例

: 配列 [10, 80, 30, 90, 40, 50, 70] を昇順にソートする。

  1. ピボットとして最後の要素 70 を選択
  2. 70 より小さい要素を左、70 より大きい要素を右に配置: [10, 30, 40, 50, 70, 80, 90]
  3. 再帰的に左と右の部分配列をソート

Pythonの例

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + middle + quick_sort(right)

# 使用例
arr = [3, 6, 8, 10, 1, 2, 1]
print("Sorted array:", quick_sort(arr))

6. ヒープソート(Heap Sort)

  • 概要: 配列をヒープ構造に変換し、最大(または最小)ヒープを使ってソートを行う手法。
  • 利点: 安定したパフォーマンスを持ち、メモリ効率も良い。
  • 欠点: 実装が複雑。

現実の例にすると

山積みの書類から一番重要な書類を見つけて取り出し、それを別の山に積み替える。これを繰り返して、重要度順に書類を並べ替える。

といった並べ替えの方法です。

ヒープソートの具体的な例

: 配列 [12, 11, 13, 5, 6, 7] を昇順にソートする。

  1. 最大ヒープを構築: [13, 11, 12, 5, 6, 7]
  2. ヒープから最大値を取り出し、残りの部分で再びヒープを構築し、これを繰り返す。

Pythonの例

def heapify(arr, n, i):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2

    if left < n and arr[i] < arr[left]:
        largest = left

    if right < n and arr[largest] < arr[right]:
        largest = right

    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

def heap_sort(arr):
    n = len(arr)

    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)

    for i in range(n-1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr, i, 0)

    return arr

# 使用例
arr = [12, 11, 13, 5, 6, 7]
print("Sorted array:", heap_sort(arr))

7. 計数ソート(Counting Sort)

  • 概要: 各要素の出現回数を数え、それに基づいて要素をソートする手法。
  • 利点: 特定の条件下で非常に高速。
  • 欠点: 使えるデータ範囲が限られる。

現実の例にすると

パーティーで配るキャンディを色ごとに分けるとき、各色のキャンディの数を数え、その数に基づいて袋に詰める。

といった並べ替えの方法です。

計数ソートの具体的な例

: 配列 [4, 2, 2, 8, 3, 3, 1] を昇順にソートする。

  1. 各要素の出現回数をカウント: 1->1, 2->2, 3->2, 4->1, 8->1
  2. カウントに基づき、ソートされた配列を作成: [1, 2, 2, 3, 3, 4, 8]

Pythonの例

def counting_sort(arr):
    max_val = max(arr)
    count = [0] * (max_val + 1)
    
    for num in arr:
        count[num] += 1
    
    sorted_arr = []
    for i, cnt in enumerate(count):
        sorted_arr.extend([i] * cnt)
    
    return sorted_arr

# 使用例
arr = [4, 2, 2, 8, 3, 3, 1]
print("Sorted array:", counting_sort(arr))

処理速度の違いや状況別おすすめソート

ソートアルゴリズムの速度は、データの種類やサイズ、既にソートされている度合いなどによって異なります。

参照元:各ソートアルゴリズムのスピード検証 | 株式会社CAM

一般的に速いソートアルゴリズム

  • クイックソート(Quick Sort): 平均的に非常に高速で、時間計算量は O(nlog⁡n) です。ただし、最悪の場合は O(n2) になることがあります。大規模なデータセットに適しています。
  • マージソート(Merge Sort): 安定したソートアルゴリズムで、時間計算量は常に O(nlog⁡n) です。追加のメモリが必要ですが、大規模なデータセットに対して一貫した性能を発揮します。
  • ヒープソート(Heap Sort): 時間計算量は O(nlog⁡n) で、メモリ使用量が少ないため、メモリ制約がある場合に適しています。

特定の状況でのおすすめソート

  • 小規模なデータセット: 挿入ソート(Insertion Sort)やバブルソート(Bubble Sort)が適しています。これらは実装が簡単で、小規模なデータに対しては効率的です。
  • 部分的にソートされたデータ: 挿入ソート(Insertion Sort)が効果的です。既にソートされている部分が多い場合、非常に高速に動作します。
  • 安定性が必要な場合: マージソート(Merge Sort)やバブルソート(Bubble Sort)が適しています。これらは安定なソートアルゴリズムで、同じ値の要素の順序を保持します。
  • メモリ使用量が制約されている場合: ヒープソート(Heap Sort)やクイックソート(Quick Sort)が適しています。これらはインプレースソートで、追加のメモリをほとんど使用しません。

それぞれのアルゴリズムには特性や適用範囲があるため、状況に応じて最適なものを選ぶことが重要です。

まとめ

今回のポイントをまとめます。

  • ソートアルゴリズムは、データを特定の順序に並べ替えるための手法
  • ソートアルゴリズムの種類
    1. バブルソート(Bubble Sort)
    2. 選択ソート(Selection Sort)
    3. 挿入ソート(Insertion Sort)
    4.  マージソート(Merge Sort)
    5.  クイックソート(Quick Sort)
    6.  ヒープソート(Heap Sort)
    7. 計数ソート(Counting Sort)

主要なほかのアルゴリズムについて知りたい方はこちら

スポンサーリンク

コメント

タイトルとURLをコピーしました