C++ Learning

第54回 ソート系アルゴリズム ★目玉

<algorithm> のソート関連関数を用途別に整理。全体ソート、安定ソート、部分ソート、k 番目だけ確定、ソート済み判定、分割。ラムダと組み合わせて自作30行を1行に圧縮する威力を見ます。

このページで押さえること
✅ 最低限ここだけ覚える
  • std::sort ― 全体ソート(既定)
  • std::stable_sort ― 同順位の順を保つ
  • std::partial_sort ― トップ N だけ
  • std::nth_element ― N 番目だけ正しい位置に
⭐ 余裕があれば読む
  • std::is_sorted / is_sorted_until
  • std::partition / stable_partition
  • 比較関数のカスタマイズ(ラムダ)
  • 計算量の違い

1. 一覧表

関数用途計算量
std::sort全体を並べ替え(既定)O(n log n)
std::stable_sort同順位の相対順序を保つソートO(n log² n) または O(n log n)
std::partial_sort先頭 k 個だけソート(残りは未定順)O(n log k)
std::nth_elementk 番目の要素だけ正しい位置にO(n) 平均
std::is_sortedソート済みか判定O(n)
std::partition述語で 2 つに分けるO(n)

2. std::sort ― 全体ソート

基本と降順sort
std::vector<int> v = {5, 2, 8, 1, 9}; std::sort(v.begin(), v.end()); // {1, 2, 5, 8, 9} std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; }); // {9, 8, 5, 2, 1} // 構造体の特定フィールドでソート struct User { std::string name; int age; }; std::vector<User> us; std::sort(us.begin(), us.end(), [](auto& a, auto& b){ return a.age < b.age; });

3. stable_sort ― 同順位の順を保つ

点数でソートするが、同じ点数なら入力順を保ちたい、という場合。

安定ソートstable
std::vector<std::pair<int, std::string>> v = { {90, "Alice"}, {80, "Bob"}, {90, "Carol"} }; std::stable_sort(v.begin(), v.end(), [](auto& a, auto& b){ return a.first > b.first; }); // {{90,Alice}, {90,Carol}, {80,Bob}} ← Alice と Carol の順が維持される // std::sort を使うと同点の順序が保証されない

4. partial_sort / nth_element ― 部分的な整列

partial_sortトップ K
std::vector<int> v = {5,2,8,1,9,3,7}; std::partial_sort( v.begin(), v.begin() + 3, v.end()); // 先頭 3 個だけソート // [1, 2, 3, ...] (残りは未定順)
nth_elementk 番目のみ
std::vector<int> v = {5,2,8,1,9,3,7}; size_t k = v.size() / 2; std::nth_element( v.begin(), v.begin() + k, v.end()); // v[k] が中央値(の位置)になる // 他の要素は未定順だが、k より小は左に
使い分け:
  • トップ 10 だけ表示したい → partial_sort
  • 中央値/パーセンタイルを一発で → nth_element
  • 全部並べる必要がない → sort より高速

5. partition / stable_partition

条件で 2 分割partition
std::vector<int> v = {1,2,3,4,5,6}; auto mid = std::partition(v.begin(), v.end(), [](int x){ return x % 2 == 0; }); // 偶数が前、奇数が後(順序は保証されない) // [2, 4, 6 | 1, 3, 5] など // mid は仕切り位置のイテレータ // 順序を保ちたければ stable_partition
ここまででソート系は OK
次章は探索系(find / binary_search / lower_bound)。

6. 実行可能デモ

▶ アルゴリズムを切り替えて結果を見る
v = {5, 2, 8, 1, 9, 3, 7, 4, 6}
広告スペース

確認クイズ

ソート系を 4 問で確認。

Q1. 同じ値の要素の入力順を保ちたいソートは?

std::sort
std::stable_sort
std::partial_sort
std::nth_element
std::sort は不安定、stable_sort は安定(同順位の相対順序を保つ)。

Q2. 配列から最大の 3 個だけ取り出したい。使うアルゴリズムは?

sort 全体
partial_sort または nth_element
stable_sort
partition
全ソートは無駄。partial_sort なら先頭 3 個だけ正確に、他は未定順。nth_element ならさらに速く(ただし先頭 3 個も未定順)。

Q3. std::sort の平均計算量は?

O(n)
O(n log n)
O(n²)
O(log n)
規格で O(n log n) が要求され、多くの実装は introsort で最悪も O(n log n)。

Q4. 配列を「偶数」と「奇数」に分けたい(順序は問わない)。

sort
stable_sort
partition
nth_element
partition は述語で要素を 2 つに分ける O(n) アルゴリズム。順序を保ちたいなら stable_partition。
← 前の講座
第52回 ラムダ式