C++ STL 알고리즘 완전 가이드
개요 — STL 알고리즘을 써야 하는 이유
Section titled “개요 — STL 알고리즘을 써야 하는 이유”원시 for 루프 대신 STL 알고리즘을 사용하면 다음 이점이 있습니다.
| 비교 | 원시 for | STL 알고리즘 |
|---|---|---|
| 의도 표현 | 암묵적 | 함수 이름이 의도를 명시 |
| 오류 가능성 | 경계 오류, off-by-one | 반복자 범위로 안전 |
| 최적화 | 수동 | 내부적으로 최적화됨 |
| 병렬화 (C++17) | 직접 구현 | std::execution::par |
대부분의 알고리즘은 <algorithm> 헤더에 있으며, 수치 알고리즘은 <numeric>에 있습니다.
1. 정렬 알고리즘
Section titled “1. 정렬 알고리즘”1.1 std::sort
Section titled “1.1 std::sort”#include <algorithm>#include <vector>
std::vector<int> Nums = {5, 2, 8, 1, 9, 3};
// 기본 오름차순 정렬 (평균 O(n log n))std::sort(Nums.begin(), Nums.end());// {1, 2, 3, 5, 8, 9}
// 내림차순std::sort(Nums.begin(), Nums.end(), std::greater<int>{});// {9, 8, 5, 3, 2, 1}
// 람다 비교자std::vector<std::string> Words = {"banana", "apple", "cherry", "date"};std::sort(Words.begin(), Words.end(), [](const std::string& A, const std::string& B){ return A.size() < B.size(); // 길이 오름차순});// {"date", "apple", "banana", "cherry"}1.2 std::stable_sort — 동일 요소 순서 유지
Section titled “1.2 std::stable_sort — 동일 요소 순서 유지”struct Player { std::string Name; int Score; };
std::vector<Player> Players = { {"Alice", 100}, {"Bob", 80}, {"Carol", 100}, {"Dave", 80}};
// 점수 내림차순, 동점자 원래 순서 유지std::stable_sort(Players.begin(), Players.end(), [](const Player& A, const Player& B) { return A.Score > B.Score; });// Alice(100), Carol(100), Bob(80), Dave(80)1.3 std::partial_sort — 앞 N개만 정렬
Section titled “1.3 std::partial_sort — 앞 N개만 정렬”std::vector<int> Data = {5, 2, 8, 1, 9, 3, 7, 4};
// 앞 3개만 정렬 (상위 3개 추출)std::partial_sort(Data.begin(), Data.begin() + 3, Data.end());// {1, 2, 3, 8, 9, 7, 5, 4} — 앞 3개만 정렬됨
// nth_element — N번째 요소만 올바른 위치로 (O(n))std::nth_element(Data.begin(), Data.begin() + 3, Data.end());// Data[3]에는 정렬 시 4번째가 올 원소가 위치 (나머지는 미정)2. 검색 알고리즘
Section titled “2. 검색 알고리즘”2.1 std::find / std::find_if
Section titled “2.1 std::find / std::find_if”std::vector<int> Nums = {1, 5, 3, 8, 2, 7};
// 값으로 검색 (O(n))auto It = std::find(Nums.begin(), Nums.end(), 8);if (It != Nums.end()){ std::cout << "찾음: " << *It << " at index " << std::distance(Nums.begin(), It);}
// 조건으로 검색auto BigIt = std::find_if(Nums.begin(), Nums.end(), [](int N) { return N > 6; }); // 첫 번째 6 초과 원소
// find_if_not — 조건을 만족하지 않는 첫 번째auto SmallIt = std::find_if_not(Nums.begin(), Nums.end(), [](int N) { return N < 5; }); // 처음으로 5 이상인 원소2.2 std::binary_search / std::lower_bound
Section titled “2.2 std::binary_search / std::lower_bound”정렬된 범위에서 이진 탐색 (O(log n)).
std::vector<int> Sorted = {1, 2, 3, 5, 8, 13, 21};
// 존재 여부만 확인bool bFound = std::binary_search(Sorted.begin(), Sorted.end(), 8); // true
// 삽입 위치 반환 (첫 번째 >= 값)auto LBound = std::lower_bound(Sorted.begin(), Sorted.end(), 6);// *LBound == 8 (6 이상의 첫 번째 원소)
// 첫 번째 > 값auto UBound = std::upper_bound(Sorted.begin(), Sorted.end(), 8);// *UBound == 13
// 동일한 값의 범위 [lower, upper)auto [Lo, Hi] = std::equal_range(Sorted.begin(), Sorted.end(), 5);// Lo → 5의 위치, Hi → 5 다음 위치2.3 std::count / std::count_if
Section titled “2.3 std::count / std::count_if”std::vector<int> Data = {1, 2, 3, 2, 5, 2, 7};
int TwoCount = std::count(Data.begin(), Data.end(), 2); // 3
int EvenCount = std::count_if(Data.begin(), Data.end(), [](int N) { return N % 2 == 0; }); // 33. 변환 알고리즘
Section titled “3. 변환 알고리즘”3.1 std::transform
Section titled “3.1 std::transform”std::vector<int> Nums = {1, 2, 3, 4, 5};std::vector<int> Doubled(Nums.size());
// 단항 변환 — 각 원소에 함수 적용std::transform(Nums.begin(), Nums.end(), Doubled.begin(), [](int N) { return N * 2; });// Doubled = {2, 4, 6, 8, 10}
// 이항 변환 — 두 범위를 합성std::vector<int> Other = {10, 20, 30, 40, 50};std::vector<int> Sum(Nums.size());std::transform(Nums.begin(), Nums.end(), Other.begin(), Sum.begin(), [](int A, int B) { return A + B; });// Sum = {11, 22, 33, 44, 55}3.2 std::for_each
Section titled “3.2 std::for_each”std::vector<int> Nums = {1, 2, 3, 4, 5};
// 부수 효과를 위한 순회 (반환값 없음)std::for_each(Nums.begin(), Nums.end(), [](int& N) { N *= 2; });// Nums = {2, 4, 6, 8, 10}
// C++17: std::for_each_n — 앞 N개만std::for_each_n(Nums.begin(), 3, [](int& N) { N = 0; });// Nums = {0, 0, 0, 8, 10}3.3 std::copy / std::copy_if
Section titled “3.3 std::copy / std::copy_if”std::vector<int> Source = {1, 2, 3, 4, 5, 6, 7, 8};std::vector<int> Dest;
// 조건에 맞는 원소만 복사std::copy_if(Source.begin(), Source.end(), std::back_inserter(Dest), [](int N) { return N % 2 == 0; });// Dest = {2, 4, 6, 8}4. 분할·제거 알고리즘
Section titled “4. 분할·제거 알고리즘”4.1 std::partition
Section titled “4.1 std::partition”std::vector<int> Nums = {1, 5, 2, 8, 3, 9, 4};
// 조건 true 원소를 앞으로, false를 뒤로 (순서 불안정)auto Mid = std::partition(Nums.begin(), Nums.end(), [](int N) { return N % 2 == 0; });// 짝수들이 앞으로: {2, 4, 8, 5, 3, 9, 1} (순서 보장 안됨)// Mid는 홀수 시작 반복자
// stable_partition — 상대 순서 유지auto Mid2 = std::stable_partition(Nums.begin(), Nums.end(), [](int N) { return N % 2 == 0; });// {2, 8, 4, 1, 5, 3, 9}4.2 std::remove_if (erase-remove 패턴)
Section titled “4.2 std::remove_if (erase-remove 패턴)”std::vector<int> Nums = {1, 2, 3, 4, 5, 6};
// remove_if는 원소를 실제 삭제하지 않고 뒤로 밀고 새 끝 반복자 반환auto NewEnd = std::remove_if(Nums.begin(), Nums.end(), [](int N) { return N % 2 == 0; }); // 짝수 제거
// erase로 실제 삭제 (erase-remove 패턴)Nums.erase(NewEnd, Nums.end());// Nums = {1, 3, 5}
// C++20: std::erase_if — 한 줄로std::erase_if(Nums, [](int N) { return N > 2; });// Nums = {1}5. 수치 알고리즘 (<numeric>)
Section titled “5. 수치 알고리즘 (<numeric>)”#include <numeric>
std::vector<int> Nums = {1, 2, 3, 4, 5};
// 합산 (초기값 0)int Total = std::accumulate(Nums.begin(), Nums.end(), 0); // 15
// 곱셈으로 변경int Product = std::accumulate(Nums.begin(), Nums.end(), 1, [](int Acc, int N) { return Acc * N; }); // 120
// 인접 차이std::vector<int> Diffs(Nums.size());std::adjacent_difference(Nums.begin(), Nums.end(), Diffs.begin());// Diffs = {1, 1, 1, 1, 1}
// 누적 합std::vector<int> Cumulative(Nums.size());std::inclusive_scan(Nums.begin(), Nums.end(), Cumulative.begin());// Cumulative = {1, 3, 6, 10, 15}
// iota — 연속 값 채우기std::vector<int> Seq(10);std::iota(Seq.begin(), Seq.end(), 0); // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}6. C++20 Ranges — 선언적 파이프라인
Section titled “6. C++20 Ranges — 선언적 파이프라인”Ranges는 알고리즘을 파이프라인으로 연결해 더 읽기 쉬운 코드를 작성하게 합니다.
#include <ranges>#include <vector>#include <iostream>
std::vector<int> Nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 파이프라인: 짝수만 필터링 → 제곱 → 처음 3개auto Result = Nums | std::views::filter([](int N) { return N % 2 == 0; }) // {2, 4, 6, 8, 10} | std::views::transform([](int N) { return N * N; }) // {4, 16, 36, 64, 100} | std::views::take(3); // {4, 16, 36}
for (int V : Result){ std::cout << V << " "; // 4 16 36}주요 Views
Section titled “주요 Views”// take / drop — 앞 N개 / 건너뛰기auto First3 = Nums | std::views::take(3); // {1, 2, 3}auto Skip3 = Nums | std::views::drop(3); // {4, 5, 6, 7, 8, 9, 10}
// iota — 정수 범위 생성 (컨테이너 불필요)for (int N : std::views::iota(1, 6)) { std::cout << N << " "; } // 1 2 3 4 5
// reversefor (int N : Nums | std::views::reverse) { std::cout << N << " "; } // 10 9 8 ...
// zip (C++23)// std::vector<int> A = {1, 2, 3}, B = {4, 5, 6};// for (auto [a, b] : std::views::zip(A, B)) { ... }// Ranges 알고리즘 (반복자 쌍 대신 범위 직접 전달)std::vector<int> Data = {5, 2, 8, 1, 9};
std::ranges::sort(Data); // 정렬auto It = std::ranges::find(Data, 8); // 검색bool bAny = std::ranges::any_of(Data, [](int N) { return N > 7; }); // 조건 확인7. 조건 확인 알고리즘
Section titled “7. 조건 확인 알고리즘”std::vector<int> Nums = {2, 4, 6, 8};
// 모든 원소가 조건을 만족하는가?bool bAllEven = std::all_of(Nums.begin(), Nums.end(), [](int N) { return N % 2 == 0; }); // true
// 하나라도 조건을 만족하는가?bool bAnyOver5 = std::any_of(Nums.begin(), Nums.end(), [](int N) { return N > 5; }); // true
// 어떤 원소도 조건을 만족하지 않는가?bool bNoneNeg = std::none_of(Nums.begin(), Nums.end(), [](int N) { return N < 0; }); // true| 목적 | 함수 |
|---|---|
| 정렬 | sort, stable_sort, partial_sort |
| 검색 | find, find_if, binary_search, lower_bound |
| 개수 | count, count_if |
| 변환 | transform, for_each, copy_if |
| 분할·제거 | partition, remove_if + erase |
| 수치 | accumulate, iota, inclusive_scan |
| 조건 | all_of, any_of, none_of |
| 파이프라인 | views::filter, views::transform, views::take |
핵심 규칙:
remove_if는 실제 삭제를 하지 않으므로 반드시erase와 쌍으로 사용 (erase-remove 패턴)- 정렬된 범위에는
binary_search/lower_bound(O(log n)), 미정렬이면find(O(n)) - C++20 Ranges는 지연 평가(lazy evaluation) — 순회 전까지 계산하지 않음
std::ranges::버전은 컨테이너를 직접 전달 가능해 더 간결