C++: STL アルゴリズム

レッスン34では STL コンテナを学びました、データを格納するだけ。

でもコンテナに対して操作が必要 —— 検索、ソート、変換……

自分で書くとバグが起きやすい;STL アルゴリズムを使うと、簡潔・安全


1. STL アルゴリズムの概要

(1) 1.1 STL アルゴリズムとは?

STL アルゴリズムは C++ 標準ライブラリの汎用関数テンプレート、コンテナの型に依存しない。

STL アルゴリズムを使うメリット:

自分で書く STL アルゴリズム
ループを書く必要がある コードが簡潔
バグが起きやすい テスト済み
パフォーマンスが不安 最適化済み
コードが読みにくい コードが読みやすい

クラス分け:


(2) 1.2 アルゴリズムのヘッダファイル

STL アルゴリズムは algorithm ヘッダファイルにある、数値アルゴリズムは numeric

▶ サンプル 2:STL アルゴリズムの活用(難易度 ⭐)

TEXT 📖 参照専用
#include <algorithm> // アルゴリズム用
#include <numeric>   // 数値演算(accumulate)

(3) 1.3 アルゴリズムの分類

STL アルゴリズムは関数の種類で分類:

分類 アルゴリズム 説明
非変更アルゴリズム findcountfor_each コンテナを変更しない
変更アルゴリズム copytransformreplace コンテナを変更
ソートアルゴリズム sortstable_sortpartial_sort ソート
検索アルゴリズム binary_searchlower_bound ソート済みコンテナで検索
マージアルゴリズム mergeinplace_merge 2つのソート済み範囲をマージ
数値アルゴリズム accumulateinner_product 数値計算
集合アルゴリズム set_unionset_intersection 集合演算


2. 非変更アルゴリズム

(1) 2.1 find —— 検索

機能: コンテナで値を検索、イテレータを返す。

関数シグネチャ:

CPP
InputIt find(InputIt first, InputIt last, const T& value);

例:値を検索(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88};
    
    // 90を検索
    auto it = std::find(scores.begin(), scores.end(), 90);
    
    if (it != scores.end()) {
        // 見つかった(インデックスを計算)
        int index = std::distance(scores.begin(), it);
        std::cout << "90が見つかりました、位置:" << index << std::endl;
    } else {
        std::cout << "90が見つかりませんでした" << std::endl;
    }
    
    return 0;
}

出力:

TEXT 📖 参照専用
90が見つかりました、位置:3
💡 ヒント:

  • 見つからない場合 last(つまり end())を返す
  • 時間計算量:O(n)

(2) 2.2 count —— カウント

機能: コンテナで指定した値の個数をカウント。

例:カウント(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {100, 85, 100, 92, 78, 100};
    
    // 満点の数(100点)
    int perfect = std::count(scores.begin(), scores.end(), 100);
    
    std::cout << "満点の数:" << perfect << std::endl; // 出力:3
    
    return 0;
}

(3) 2.3 for_each —— 各要素に処理

機能: コンテナの各要素に関数を適用。

例:各要素に処理(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88};
    
    // ラムダで出力
    std::for_each(scores.begin(), scores.end(), [](int s) {
        std::cout << s << " ";
    });
    std::cout << std::endl;
    
    return 0;
}

出力:

TEXT 📖 参照専用
85 92 78 90 88 
💡 ヒント:

  • for_each は手書きループより簡潔
  • ラムダと組み合わせる


3. 変更アルゴリズム

(1) 3.1 copy —— コピー

機能: 範囲の要素をコピー。

例:配列をコピー(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst(5); // あらかじめサイズ確保、5要素
    
    // コピー
    std::copy(src.begin(), src.end(), dst.begin());
    
    // 出力
    for (int x : dst) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

(2) 3.2 transform —— 変換

機能: 範囲の要素を変換。

例:変換(難易度 ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88};
    std::vector<int> adjusted(scores.size()); // 結果用
    
    // 70%を残し、30%を補正
    std::transform(scores.begin(), scores.end(), adjusted.begin(),
        [](int s) { return s * 0.7 + 90 * 0.3; });
    
    std::cout << "補正後:";
    for (int x : adjusted) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

出力:

TEXT 📖 参照専用
補正後:86.5 91.9 81.6 90 88.6

(3) 3.3 replace —— 置換

機能: コンテナで指定した値の要素を別の値に置換。

例:置換(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88};
    
    // 不合格(<60)を60に置換(補正)
    std::replace_if(scores.begin(), scores.end(),
        [](int s) { return s < 60; },
        60);
    
    std::cout << "補正後:";
    for (int x : scores) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    return 0;
}


4. ソートアルゴリズム

(1) 4.1 sort —— ソート

機能: コンテナをソート(デフォルトは昇順)。

例:ソート(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88};
    
    // 昇順ソート
    std::sort(scores.begin(), scores.end());
    
    std::cout << "昇順:";
    for (int x : scores) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    // 降順ソート
    std::sort(scores.begin(), scores.end(), std::greater<int>());
    
    std::cout << "降順:";
    for (int x : scores) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

出力:

TEXT 📖 参照専用
昇順:78 85 88 90 92
降順:92 90 88 85 78

(2) 4.2 カスタムソート

例:点数でソート(難易度 ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

struct Student {
    std::string name;
    int score;
};

int main() {
    std::vector<Student> students = {
        {"Alice", 85},
        {"Bob", 92},
        {"Charlie", 78}
    };
    
    // 点数の降順でソート
    std::sort(students.begin(), students.end(),
        [](const Student& a, const Student& b) {
            return a.score > b.score;
        });
    
    std::cout << "ランキング:" << std::endl;
    for (const auto& s : students) {
        std::cout << s.name << ":" << s.score << std::endl;
    }
    
    return 0;
}

出力:

TEXT 📖 参照専用
ランキング:
Bob:92
Alice:85
Charlie:78


5. 数値アルゴリズム

(1) 5.1 accumulate —— 累積

機能: 範囲の要素の和を計算。

例:累積(難易度 ⭐)

CPP
#include <iostream>
#include <vector>
#include <numeric>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88};
    
    // 合計点
    int total = std::accumulate(scores.begin(), scores.end(), 0);
    
    std::cout << "合計点:" << total << std::endl; // 出力:433
    std::cout << "平均点:" << total / 5.0 << std::endl; // 出力:86.6
    
    return 0;
}

(2) 5.2 inner_product —— 内積

例:2つの vector の内積(難易度 ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <numeric>

int main() {
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = {4, 5, 6};
    
    // 内積:1*4 + 2*5 + 3*6 = 32
    int dot_product = std::inner_product(v1.begin(), v1.end(), v2.begin(), 0);
    
    std::cout << "内積:" << dot_product << std::endl; // 出力:32
    
    return 0;
}


6. 総合例

▶ サンプル 1:成績分析システム(難易度 ⭐⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>

int main() {
    std::vector<int> scores = {85, 92, 78, 90, 88, 76, 95, 83, 89, 91};
    
    // 1. 学生数
    int count = scores.size();
    std::cout << "学生数:" << count << std::endl;
    
    // 2. 合計と平均
    int total = std::accumulate(scores.begin(), scores.end(), 0);
    double average = static_cast<double>(total) / count;
    std::cout << "合計点:" << total << "、平均点:" << average << std::endl;
    
    // 3. 最高と最低を検索
    int max_score = *std::max_element(scores.begin(), scores.end());
    int min_score = *std::min_element(scores.begin(), scores.end());
    std::cout << "最高点:" << max_score << "、最低点:" << min_score << std::endl;
    
    // 4. 合格者数
    int passed = std::count_if(scores.begin(), scores.end(),
        [](int s) { return s >= 60; });
    std::cout << "合格者数:" << passed << std::endl;
    
    // 5. ソートして上位3名を出力
    std::vector<int> top3 = scores;
    std::sort(top3.begin(), top3.end(), std::greater<int>());
    std::cout << "上位3名:";
    for (int i = 0; i < 3; i++) {
        std::cout << top3[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
学生数:10
合計点:867、平均点:86.7
最高点:95、最低点:76
合格者数:10
上位3名:95 92 91


7. アルゴリズムの使い方

(1) 7.1 イテレータ関数

関数 機能
std::distance(first, last) イテレータの距離
std::advance(it, n) イテレータを n 進める
std::next(it) 次のイテレータを返す
std::prev(it) 前のイテレータを返す

(2) 7.2 ラムダの詳細

ラムダは STL アルゴリズムの強力なツール:

CPP
// 基本構文
[capture](parameters) -> return_type { body }

// 例:複数条件でソート
std::sort(students.begin(), students.end(),
    [](const Student& a, const Student& b) {
        if (a.score != b.score)
            return a.score > b.score; // 点数の降順
        return a.name < b.name; // 名前の昇順
    });

▶ サンプル 3:削除パターン(難易度 ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {85, 45, 92, 58, 78, 30, 95};

    // 60点未満を削除(remove-erase イディオム)
    auto new_end = std::remove_if(scores.begin(), scores.end(),
        [](int s) { return s < 60; });
    scores.erase(new_end, scores.end());

    std::cout << "合格者: ";
    for (int s : scores) {
        std::cout << s << " ";
    }
    std::cout << std::endl;

    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
合格者: 85 92 78 95

❓ よくある質問

Q: STL アルゴリズムと手書きループの違いは? A: STL アルゴリズムを使うメリット:

  • 最適化済み
  • コンテナの型に依存しない
  • コンパイラの最適化が効きやすい

Q: どのコンテナでも STL アルゴリズムを使える? A: はい、でも注意:

  • 順序コンテナ(vectordeque):全部使える
  • 連想コンテナ(setmap):メンバー関数がある、メンバー関数を使う

Q: ラムダとは? A: ラムダは C++11 のインライン関数、関数の中で関数を定義。

基本構文:

CPP
[capture](params) -> return_type { body }

例:

CPP
auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 5) << std::endl; // 出力:8

Q: アルゴリズムでよくあるエラーは? A: よくあるエラー:

  1. コンテナがソートされていないのに検索アルゴリズムを使う → ソート必須
  2. 出力先のコンテナが小さい → back_inserter を使う
  3. イテレータの型が違う → コンテナに合わせる

📖 まとめ

分類 アルゴリズム
STL アルゴリズム 汎用関数テンプレート、コンテナに依存しない
非変更アルゴリズム findcountfor_each
変更アルゴリズム copytransformreplace
ソートアルゴリズム sort + カスタム関数
数値アルゴリズム accumulate(累積)
ラムダ インライン関数、アルゴリズムで活用

推奨:


📝 練習問題

  1. 初級(難易度 ⭐): std::vector<int> に10個のランダムな整数を入れ、sort でソートして出力、reverse で逆順に出力。

  2. 中級(難易度 ⭐⭐): findstd::vector<std::string> から文字列を検索、count で特定の値の個数をカウント。

  3. 上級(難易度 ⭐⭐⭐): remove_if とラムダで「vector から奇数を削除」を実装。erase-remove の使い方を理解。


次のレッスン: 練習:OOP総合(#36) —— オブジェクト指向で学生管理システムをリファクタリング

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%