C++ Algorithm MinMax | "최소/최대 알고리즘" 가이드
이 글의 핵심
std::min·max·minmax, min_element·max_element, C++17 clamp, 실전 패턴과 성능 팁을 한 글에 정리합니다.
MinMax 알고리즘이란?
MinMax 알고리즘은 최소값과 최대값을 찾는 STL 알고리즘입니다. 값 비교와 범위 검색 두 가지 방식을 제공합니다.
#include <algorithm>
int a = 3, b = 5;
// 최소
int minVal = std::min(a, b); // 3
// 최대
int maxVal = std::max(a, b); // 5
// 둘 다
auto [minV, maxV] = std::minmax(a, b);
왜 필요한가?:
- 간결성: 조건문 없이 최소/최대
- 안전성: 타입 안전한 비교
- 성능: 최적화된 구현
- 편의성: 범위 검색 지원
// ❌ 수동 비교: 복잡
int minVal;
if (a < b) {
minVal = a;
} else {
minVal = b;
}
// ✅ std::min: 간결
int minVal = std::min(a, b);
MinMax 알고리즘 종류:
| 알고리즘 | 입력 | 반환 | 시간 복잡도 |
|---|---|---|---|
min(a, b) | 2개 값 | 최소값 | O(1) |
max(a, b) | 2개 값 | 최대값 | O(1) |
minmax(a, b) | 2개 값 | {최소, 최대} | O(1) |
min_element(begin, end) | 범위 | 최소 반복자 | O(n) |
max_element(begin, end) | 범위 | 최대 반복자 | O(n) |
minmax_element(begin, end) | 범위 | {최소, 최대} 반복자 | O(n) |
clamp(v, lo, hi) | 값, 범위 | 제한된 값 | O(1) |
// 값 비교
int minVal = std::min(3, 5);
int maxVal = std::max(3, 5);
auto [minV, maxV] = std::minmax(3, 5);
// 범위 검색
std::vector<int> v = {3, 1, 4, 1, 5};
auto minIt = std::min_element(v.begin(), v.end());
auto maxIt = std::max_element(v.begin(), v.end());
auto [minIt2, maxIt2] = std::minmax_element(v.begin(), v.end());
// 범위 제한
int clamped = std::clamp(150, 0, 100); // 100
std::min, std::max, std::minmax (값 두 개·여러 개)
std::min(a, b)/std::max(a, b): 두 인자를 비교해 작은/큰 값을 참조로 돌려줍니다(임시 객체와 조합할 때 댕글링 주의 — FAQ 참고).std::minmax(a, b):std::pair에 최소·최대를 담아 한 번의 비교 묶음으로 둘 다 얻습니다. 분리해서min다음max를 호출하는 것보다 의도가 분명합니다.std::min({a,b,c,...}):initializer_listoverload로 여러 값 중 최소를 구합니다. 가변 인자를 직접 펼치기 어려울 때 유용합니다.
auto [lo, hi] = std::minmax(10, 20); // lo=10, hi=20
int m = std::min({3, 1, 4, 1, 5}); // 1
min_element, max_element, minmax_element (범위)
범위 버전은 값이 아니라 반복자를 돌려줍니다. 이유는 최소/최대가 여러 번 나올 때 “첫 번째” 를 고르기 위해서입니다(동일 값이 여러 개면 가장 앞쪽 요소를 가리킴).
min_element/max_element: 단일 패스 O(n). 정렬 없이 선형 스캔입니다.minmax_element: 최소와 최대 반복자를 한 번의 순회로 구합니다. 범위를 두 번 도는 것보다 효율적일 수 있습니다.
std::vector<int> v = {3, 1, 4, 1, 5};
auto [minIt, maxIt] = std::minmax_element(v.begin(), v.end());
// *minIt == 1 (첫 번째 1), *maxIt == 5
빈 범위에 대해 min_element 등을 호출하면 UB이므로 반드시 empty() 검사를 하세요.
std::clamp (C++17) 심화
std::clamp(v, lo, hi)는 수학적으로 std::min(std::max(v, lo), hi) 와 같습니다. GUI 슬라이더, 물리 엔진 속도 상한, 색상·알파 채널처럼 구간으로 값을 묶을 때 코드가 짧아집니다.
lo <= hi가 반드시 성립해야 합니다. 그렇지 않으면 UB입니다.- 부동소수 비교에서는 NaN 전파 규칙을 확인하세요(구현은 표준에 따라
v가 NaN이면 NaN을 반환하는 등 명시되어 있습니다).
double speed = 120.0;
double capped = std::clamp(speed, 0.0, 100.0); // 100.0
C++20에는 std::midpoint 등과 함께 쓰면 수치 코드가 더 읽기 쉬워지는 경우가 있습니다.
실전 예제 보강
UI: 마우스 좌표를 화면 안으로
int x = std::clamp(raw_x, 0, screen_width - 1);
두 시점의 통계를 한 번에
auto [minIt, maxIt] = std::minmax_element(data.begin(), data.end());
if (minIt != data.end()) {
int span = *maxIt - *minIt;
}
성능 팁
min/max(두 값): 인라인되는 경우가 많아 비용 무시 수준입니다.min_elementvssort후front: 최소 하나만 필요하면 정렬하지 말고min_element가 O(n)으로 충분합니다. “전체 순서”가 필요할 때만 정렬하세요.minmax_element: 최소와 최대를 둘 다 필요하면 두 번의 선형 스캔 대신 한 번으로 묶는 편이 낫습니다.- SIMD/벡터화: 매우 큰 배열에서 극한 최적화가 필요하면 라이브러리나 컴파일러 최적화에 맡기고, 일반 코드에서는
std::min/max가 가독성과 이식성 면에서 유리합니다. - 분기 예측:
std::min/max는 분기를 포함하므로, 핫 루프에서는 데이터 패턴에 따라 프로파일 결과가 달라질 수 있습니다.
기본 사용
#include <algorithm>
#include <vector>
std::vector<int> v = {3, 1, 4, 1, 5};
// 최소 요소
auto minIt = std::min_element(v.begin(), v.end());
std::cout << "최소: " << *minIt << std::endl; // 1
// 최대 요소
auto maxIt = std::max_element(v.begin(), v.end());
std::cout << "최대: " << *maxIt << std::endl; // 5
실전 예시
예시 1: minmax_element
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
// 최소/최대 동시
auto [minIt, maxIt] = std::minmax_element(v.begin(), v.end());
std::cout << "최소: " << *minIt << std::endl; // 1
std::cout << "최대: " << *maxIt << std::endl; // 9
}
예시 2: 커스텀 비교
#include <algorithm>
#include <vector>
#include <string>
struct Person {
std::string name;
int age;
};
int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 20}
};
// 나이로 최소/최대
auto youngest = std::min_element(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
std::cout << "최연소: " << youngest->name << " (" << youngest->age << ")" << std::endl;
}
예시 3: 여러 값
#include <algorithm>
int main() {
// 여러 값 중 최소/최대
int minVal = std::min({3, 1, 4, 1, 5, 9});
int maxVal = std::max({3, 1, 4, 1, 5, 9});
std::cout << "최소: " << minVal << std::endl; // 1
std::cout << "최대: " << maxVal << std::endl; // 9
}
예시 4: clamp
#include <algorithm>
int main() {
int value = 150;
// 범위 제한 (C++17)
int clamped = std::clamp(value, 0, 100);
std::cout << "제한: " << clamped << std::endl; // 100
// min(max(value, low), high)와 동일
}
MinMax 알고리즘
// 값 비교
std::min(a, b)
std::max(a, b)
std::minmax(a, b)
// 범위 검색
std::min_element(begin, end)
std::max_element(begin, end)
std::minmax_element(begin, end)
// 범위 제한 (C++17)
std::clamp(value, low, high)
자주 발생하는 문제
문제 1: 참조
int a = 3, b = 5;
// min/max는 const 참조 반환
const int& minRef = std::min(a, b);
// ❌ 임시 객체
// const int& minRef = std::min(3, 5); // 댕글링
// ✅ 값
int minVal = std::min(3, 5);
문제 2: 빈 범위
std::vector<int> v;
// ❌ 빈 범위
// auto minIt = std::min_element(v.begin(), v.end()); // 정의되지 않은 동작
// ✅ 확인
if (!v.empty()) {
auto minIt = std::min_element(v.begin(), v.end());
}
문제 3: 여러 값
// ❌ 중첩 호출
int minVal = std::min(std::min(a, b), c);
// ✅ initializer_list
int minVal = std::min({a, b, c});
문제 4: clamp 순서
// clamp(value, low, high)
// low <= high 보장 필요
int value = 50;
// ❌ low > high
// int clamped = std::clamp(value, 100, 0); // 정의되지 않은 동작
// ✅ low <= high
int clamped = std::clamp(value, 0, 100);
활용 패턴
// 1. 최소/최대
int minVal = std::min(a, b);
int maxVal = std::max(a, b);
// 2. 범위 검색
auto minIt = std::min_element(v.begin(), v.end());
// 3. 범위 제한
int clamped = std::clamp(value, 0, 100);
// 4. 역순
std::reverse(v.begin(), v.end());
실무 패턴
패턴 1: 통계 계산
#include <algorithm>
#include <vector>
#include <numeric>
struct Statistics {
int min;
int max;
double average;
};
Statistics calculateStats(const std::vector<int>& data) {
if (data.empty()) {
return {0, 0, 0.0};
}
auto [minIt, maxIt] = std::minmax_element(data.begin(), data.end());
int sum = std::accumulate(data.begin(), data.end(), 0);
return {
*minIt,
*maxIt,
static_cast<double>(sum) / data.size()
};
}
// 사용
std::vector<int> scores = {85, 92, 78, 95, 88};
auto stats = calculateStats(scores);
std::cout << "최소: " << stats.min << '\n'; // 78
std::cout << "최대: " << stats.max << '\n'; // 95
std::cout << "평균: " << stats.average << '\n'; // 87.6
패턴 2: 범위 검증
#include <algorithm>
template<typename T>
bool inRange(T value, T low, T high) {
return value >= low && value <= high;
}
template<typename T>
T clampToRange(T value, T low, T high) {
return std::clamp(value, low, high);
}
// 사용
int score = 150;
if (!inRange(score, 0, 100)) {
score = clampToRange(score, 0, 100); // 100
}
패턴 3: 정규화
#include <algorithm>
#include <vector>
std::vector<double> normalize(const std::vector<double>& data) {
if (data.empty()) {
return {};
}
auto [minIt, maxIt] = std::minmax_element(data.begin(), data.end());
double minVal = *minIt;
double maxVal = *maxIt;
double range = maxVal - minVal;
if (range == 0.0) {
return std::vector<double>(data.size(), 0.5);
}
std::vector<double> result;
result.reserve(data.size());
for (double value : data) {
result.push_back((value - minVal) / range);
}
return result;
}
// 사용
std::vector<double> data = {10.0, 20.0, 30.0, 40.0, 50.0};
auto normalized = normalize(data);
// 결과: {0.0, 0.25, 0.5, 0.75, 1.0}
FAQ
Q1: min/max는 무엇인가요?
A: 두 값을 비교하여 최소/최대값을 반환합니다.
int minVal = std::min(3, 5); // 3
int maxVal = std::max(3, 5); // 5
Q2: min_element는?
A: 범위에서 최소 요소의 반복자를 반환합니다.
std::vector<int> v = {3, 1, 4, 1, 5};
auto minIt = std::min_element(v.begin(), v.end());
std::cout << *minIt << '\n'; // 1
Q3: minmax는?
A: 최소/최대를 동시에 반환합니다. 비교 횟수가 적습니다.
auto [minV, maxV] = std::minmax(3, 5);
// minV = 3, maxV = 5
Q4: clamp는?
A: 값을 범위로 제한합니다 (C++17).
int clamped = std::clamp(150, 0, 100); // 100
Q5: 여러 값 중 최소/최대는?
A: initializer_list 를 사용합니다.
int minVal = std::min({3, 1, 4, 1, 5}); // 1
int maxVal = std::max({3, 1, 4, 1, 5}); // 5
Q6: 빈 범위는?
A: 정의되지 않은 동작입니다. 확인이 필요합니다.
std::vector<int> v;
// ❌ 빈 범위
// auto minIt = std::min_element(v.begin(), v.end());
// ✅ 확인
if (!v.empty()) {
auto minIt = std::min_element(v.begin(), v.end());
}
Q7: 커스텀 비교는?
A: 비교 함수를 제공합니다.
struct Person {
std::string name;
int age;
};
std::vector<Person> people;
auto youngest = std::min_element(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
Q8: MinMax 학습 리소스는?
A:
- “Effective STL” by Scott Meyers
- “C++ Primer” by Stanley Lippman
- cppreference.com - min/max
관련 글: algorithm, sort, clamp.
한 줄 요약: MinMax 알고리즘은 최소값과 최대값을 찾는 STL 알고리즘입니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ Algorithm Search | “검색 알고리즘” 가이드
- C++ 알고리즘 | “STL algorithm” 핵심 정리
- C++ Algorithm Sort | “정렬 알고리즘” 가이드
실전 팁
실무에서 바로 적용할 수 있는 팁입니다.
디버깅 팁
- 문제가 발생하면 먼저 컴파일러 경고를 확인하세요
- 간단한 테스트 케이스로 문제를 재현하세요
성능 팁
- 프로파일링 없이 최적화하지 마세요
- 측정 가능한 지표를 먼저 설정하세요
코드 리뷰 팁
- 코드 리뷰에서 자주 지적받는 부분을 미리 체크하세요
- 팀의 코딩 컨벤션을 따르세요
실전 체크리스트
실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.
코드 작성 전
- 이 기법이 현재 문제를 해결하는 최선의 방법인가?
- 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
- 성능 요구사항을 만족하는가?
코드 작성 중
- 컴파일러 경고를 모두 해결했는가?
- 엣지 케이스를 고려했는가?
- 에러 처리가 적절한가?
코드 리뷰 시
- 코드의 의도가 명확한가?
- 테스트 케이스가 충분한가?
- 문서화가 되어 있는가?
이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.
이 글에서 다루는 키워드 (관련 검색어)
C++, algorithm, min, max, STL 등으로 검색하시면 이 글이 도움이 됩니다.
관련 글
- C++ Algorithm Copy |
- C++ Algorithm Count |
- C++ Algorithm Generate |
- C++ 알고리즘 |
- C++ Algorithm Heap |