🐙

【図解】アルゴリズムとは?初心者が知るべき基本概念

2025/03/07に公開2

プログラミングを学ぶ上で避けて通れない「アルゴリズム」という言葉。「なんだか難しそう…」と感じる方も多いのではないでしょうか?この記事では、アルゴリズムとは何か、なぜ重要なのか、そして基本的なアルゴリズムについて、図解を交えてわかりやすく解説します。

アルゴリズムとは?

簡単に言うと…

アルゴリズムとは、問題を解決するための手順や方法のことです。料理のレシピや組み立て説明書のようなもので、ステップバイステップで目標を達成するための道筋を示します。

例えば、「朝起きてから会社に行くまで」というシンプルな行動も、以下のようなアルゴリズムで表現できます:

コンピュータプログラミングにおけるアルゴリズムも同じ考え方です。特定の問題を解決するための、明確で順序立てられた手順の集まりなのです。

アルゴリズムの特徴

良いアルゴリズムには、以下のような特徴があります:

  1. 明確性:各ステップが明確で曖昧さがない
  2. 有限性:必ず終了する(無限ループにならない)
  3. 入力:0個以上の入力を受け取る
  4. 出力:1個以上の出力を生成する
  5. 効率性:リソース(時間・メモリなど)を効率的に使用する

なぜアルゴリズムが重要なのか?

アルゴリズムを学ぶことには、以下のようなメリットがあります:

1. 問題解決能力の向上

アルゴリズム的思考を身につけることで、複雑な問題を小さな部分に分解し、効率的に解決する能力が養われます。これはプログラミングだけでなく、日常生活のあらゆる場面で役立ちます。

2. 効率的なコードの作成

同じ問題を解決するプログラムでも、アルゴリズムの選択によって実行速度やメモリ使用量が大きく変わります。適切なアルゴリズムを選ぶことで、より効率的なプログラムを作成できます。

3. 技術面接での評価ポイント

多くのIT企業の採用面接では、アルゴリズムの知識や問題解決能力が評価されます。基本的なアルゴリズムを理解していることは、就職・転職の際に大きなアドバンテージとなります。

アルゴリズムの表現方法

アルゴリズムを表現する方法はいくつかありますが、主に以下の3つが使われます:

1. 自然言語(日本語や英語など)

最も基本的な表現方法で、普段使っている言葉でアルゴリズムを説明します。

例:「配列の中から最大値を見つけるには、まず最初の要素を最大値とし、配列の各要素と比較していき、より大きな値が見つかったら最大値を更新する」

2. フローチャート(流れ図)

アルゴリズムの流れを視覚的に表現する方法です。処理の流れが一目でわかるため、理解しやすいという特徴があります。

例:最大値を見つけるアルゴリズムのフローチャート

3. 疑似コード(擬似言語)

プログラミング言語に似た形式で書かれるが、特定の言語の文法に縛られない記述方法です。アルゴリズムの本質を表現しつつ、実装に近い形で記述できます。

例:最大値を見つける疑似コード

function findMax(array):
    if array is empty:
        return null
    
    max = array[0]
    for i from 1 to array.length - 1:
        if array[i] > max:
            max = array[i]
    
    return max

基本的なアルゴリズム

ここからは、プログラミング初心者が知っておくべき基本的なアルゴリズムをいくつか紹介します。

1. 線形探索(Linear Search)

配列の中から特定の値を探すシンプルなアルゴリズムです。先頭から順番に各要素をチェックしていきます。

図解

疑似コード

function linearSearch(array, target):
    for i from 0 to array.length - 1:
        if array[i] equals target:
            return i  // 見つかった位置を返す
    
    return -1  // 見つからなかった

特徴

  • 時間複雑度: O(n) - 最悪の場合、配列の全要素を調べる必要がある
  • 空間複雑度: O(1) - 追加のメモリをほとんど使用しない
  • メリット: シンプルで実装が容易、配列がソートされていなくても使える
  • デメリット: 大きな配列では効率が悪い

2. 二分探索(Binary Search)

ソート済みの配列で使える効率的な探索アルゴリズムです。配列の中央の値と探したい値を比較し、探索範囲を半分に絞っていきます。

図解

もう少し複雑な例:

疑似コード

function binarySearch(array, target):
    left = 0
    right = array.length - 1
    
    while left <= right:
        mid = (left + right) / 2
        
        if array[mid] equals target:
            return mid  // 見つかった位置を返す
        else if array[mid] < target:
            left = mid + 1  // 右半分を探索
        else:
            right = mid - 1  // 左半分を探索
    
    return -1  // 見つからなかった

特徴

  • 時間複雑度: O(log n) - 各ステップで探索範囲が半分になる
  • 空間複雑度: O(1) - 追加のメモリをほとんど使用しない
  • メリット: 大きな配列でも効率的に探索できる
  • デメリット: 配列がソートされている必要がある

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

最も基本的なソートアルゴリズムの一つです。隣接する要素を比較し、必要に応じて交換することで、徐々に大きな値を「泡」のように右側に移動させていきます。

図解

疑似コード

function bubbleSort(array):
    n = array.length
    
    for i from 0 to n - 1:
        for j from 0 to n - i - 1:
            if array[j] > array[j + 1]:
                // 要素を交換
                swap array[j] and array[j + 1]
    
    return array

特徴

  • 時間複雑度: O(n²) - 二重ループを使用
  • 空間複雑度: O(1) - 追加のメモリをほとんど使用しない
  • メリット: シンプルで実装が容易、小さな配列では効率的
  • デメリット: 大きな配列では非常に効率が悪い

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

「分割統治法」を用いた効率的なソートアルゴリズムです。ピボット(基準値)を選び、それより小さい要素と大きい要素に分割し、再帰的にソートしていきます。

図解

疑似コード

function quickSort(array, low, high):
    if low < high:
        // ピボットを選び、配列を分割
        pivotIndex = partition(array, low, high)
        
        // ピボットの左側をソート
        quickSort(array, low, pivotIndex - 1)
        
        // ピボットの右側をソート
        quickSort(array, pivotIndex + 1, high)
    
    return array

function partition(array, low, high):
    pivot = array[high]  // 最後の要素をピボットとして選択
    i = low - 1
    
    for j from low to high - 1:
        if array[j] <= pivot:
            i = i + 1
            swap array[i] and array[j]
    
    swap array[i + 1] and array[high]
    return i + 1

特徴

  • 時間複雑度: 平均 O(n log n)、最悪 O(n²)
  • 空間複雑度: O(log n) - 再帰呼び出しのためのスタック領域
  • メリット: 平均的には非常に効率的、実用的なソートアルゴリズム
  • デメリット: 最悪の場合(既にソートされている配列など)は効率が悪い

5. 再帰アルゴリズム(Recursion)

再帰とは、関数が自分自身を呼び出す手法です。多くの問題を簡潔に解決できますが、理解するのが少し難しいかもしれません。

例:階乗の計算

階乗(n!)は、1からnまでの整数の積です。例えば、5! = 5 × 4 × 3 × 2 × 1 = 120

再帰を使った階乗の計算は以下のように表現できます:

  • 0! = 1 (基本ケース)
  • n! = n × (n-1)! (n > 0の場合)

図解

疑似コード

function factorial(n):
    // 基本ケース
    if n equals 0 or n equals 1:
        return 1
    
    // 再帰ケース
    return n * factorial(n - 1)

特徴

  • メリット: 複雑な問題を簡潔に表現できる
  • デメリット: スタックオーバーフローの可能性がある、理解しにくい場合がある

アルゴリズムの効率性

アルゴリズムの効率性を評価する際、主に以下の2つの観点から考えます:

1. 時間複雑度(Time Complexity)

アルゴリズムの実行時間がどのように増加するかを表します。入力サイズnに対して、O(n)、O(n²)、O(log n)などで表現します。

  • O(1): 定数時間(入力サイズに関係なく一定)
  • O(log n): 対数時間(二分探索など)
  • O(n): 線形時間(線形探索など)
  • O(n log n): 線形対数時間(効率的なソートアルゴリズムなど)
  • O(n²): 二次時間(バブルソートなど)
  • O(2^n): 指数時間(総当たり探索など)

2. 空間複雑度(Space Complexity)

アルゴリズムが使用するメモリ量がどのように増加するかを表します。

  • O(1): 定数空間(追加のメモリをほとんど使用しない)
  • O(n): 線形空間(入力サイズに比例したメモリを使用)
  • O(n²): 二次空間(二次元配列など)

実践的な例:アルゴリズムを使った問題解決

ここでは、アルゴリズムを使って実際の問題を解決する例を紹介します。

例題:最頻値(モード)を求める

配列の中で最も頻繁に出現する値(最頻値)を求めるアルゴリズムを考えてみましょう。

解法

  1. 各要素の出現回数を数える
  2. 最も出現回数が多い要素を見つける

疑似コード

function findMode(array):
    if array is empty:
        return null
    
    // 各要素の出現回数を数える
    countMap = empty map
    for each element in array:
        if element exists in countMap:
            countMap[element] = countMap[element] + 1
        else:
            countMap[element] = 1
    
    // 最頻値を見つける
    maxCount = 0
    mode = null
    
    for each (element, count) in countMap:
        if count > maxCount:
            maxCount = count
            mode = element
    
    return mode

JavaScript実装

function findMode(array) {
  if (array.length === 0) return null;
  
  // 各要素の出現回数を数える
  const countMap = {};
  for (const element of array) {
    countMap[element] = (countMap[element] || 0) + 1;
  }
  
  // 最頻値を見つける
  let maxCount = 0;
  let mode = null;
  
  for (const element in countMap) {
    if (countMap[element] > maxCount) {
      maxCount = countMap[element];
      mode = element;
    }
  }
  
  return mode;
}

// 使用例
const numbers = [1, 2, 3, 2, 2, 4, 5, 3, 2];
console.log(findMode(numbers));  // 2

アルゴリズムを学ぶためのリソース

アルゴリズムをさらに学びたい方のために、おすすめの日本語リソースをいくつか紹介します:

1. 日本語のオンラインプラットフォーム

  • Paiza: 実践的なプログラミング問題が豊富で、スキルチェックも可能
  • AtCoder: 競技プログラミングのコンテストが定期的に開催される
  • ドットインストール: 短い動画で基本的なアルゴリズムを学べる
  • Progate: 初心者向けのインタラクティブな学習プラットフォーム

2. 書籍

  • 『アルゴリズム図鑑』 石田保輝、宮崎修一 著
  • 『世界で闘うプログラミング力を鍛える150問』 Gayle Laakmann McDowell 著
  • 『問題解決力を鍛える!アルゴリズムとデータ構造』 大槻兼資 著

3. 日本語のYouTube チャンネル

  • キノコード: アルゴリズムやデータ構造を日本語でわかりやすく解説
  • しまぶーのIT大学: プログラミングの基礎からアルゴリズムまで幅広く解説
  • Tomoaki Takezoe: アルゴリズムの解説や競技プログラミングのテクニックを紹介
  • JMOOC 放送大学: 大学レベルのアルゴリズム講座が公開されている

まとめ

この記事では、アルゴリズムの基本概念から、代表的なアルゴリズム、そして効率性の評価方法まで幅広く解説しました。

アルゴリズムは、プログラミングの基礎であり、効率的なコードを書くための重要な知識です。一度にすべてを理解しようとせず、少しずつ実践しながら身につけていくことをおすすめします。

特に初心者の方は、以下のステップで学習を進めるとよいでしょう:

  1. 基本的なアルゴリズム(線形探索、バブルソートなど)を理解する
  2. 簡単な問題を解きながら、アルゴリズム的思考を身につける
  3. より効率的なアルゴリズム(二分探索、クイックソートなど)を学ぶ
  4. 実際のプロジェクトでアルゴリズムを活用する

アルゴリズムの学習は一朝一夕にはいきませんが、基本を押さえて継続的に学ぶことで、プログラミングスキルは確実に向上します。

Discussion

junerjuner

zenn は mermaid 記法サポートしているみたいなのでフローチャートはそっちにしてみた方がよいかも……?
https://zenn.dev/team_zenn/articles/zenn-mermaid-js

宝松@システムエンジニア宝松@システムエンジニア

✨ ご指摘ありがとうございます!Zenn で mermaid 記法が使えるのは知りませんでした。
せっかくなので、今後の記事では mermaid を活用してみますね!
貴重なフィードバック、助かります🙏