C++ Algorithm Replace | "치환 알고리즘" 가이드
이 글의 핵심
C++ Algorithm Replace에 대한 실전 가이드입니다.
들어가며
C++ STL의 replace 알고리즘은 컨테이너의 요소를 효율적으로 치환할 수 있게 해줍니다. 값 기반 치환과 조건 기반 치환을 모두 지원합니다.
1. std::replace
기본 사용
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 2, 4, 2, 5};
std::cout << "원본: ";
for (int x : v) {
std::cout << x << " "; // 1 2 3 2 4 2 5
}
std::cout << std::endl;
// 2를 9로 치환
std::replace(v.begin(), v.end(), 2, 9);
std::cout << "치환 후: ";
for (int x : v) {
std::cout << x << " "; // 1 9 3 9 4 9 5
}
std::cout << std::endl;
}
문자열 치환
#include <iostream>
#include <algorithm>
#include <string>
int main() {
std::string text = "Hello World";
// 공백을 밑줄로 치환
std::replace(text.begin(), text.end(), ' ', '_');
std::cout << text << std::endl; // Hello_World
// 특정 문자 치환
std::string code = "int x = 10;";
std::replace(code.begin(), code.end(), ' ', '\t');
std::cout << code << std::endl; // int x = 10;
}
함수 시그니처:
template<class ForwardIt, class T>
void replace(ForwardIt first, ForwardIt last,
const T& old_value, const T& new_value);
핵심 개념:
- 원본 수정: 컨테이너를 직접 수정
- 모든 일치: 일치하는 모든 요소 치환
- 시간 복잡도: O(n)
2. std::replace_if
조건 기반 치환
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 짝수를 0으로 치환
std::replace_if(v.begin(), v.end(),
{ return x % 2 == 0; }, 0);
for (int x : v) {
std::cout << x << " "; // 1 0 3 0 5 0 7 0 9 0
}
std::cout << std::endl;
}
복잡한 조건
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> scores = {45, 67, 89, 34, 92, 78, 56};
// 60점 미만을 0으로
std::replace_if(scores.begin(), scores.end(),
{ return score < 60; }, 0);
std::cout << "점수: ";
for (int score : scores) {
std::cout << score << " "; // 0 67 89 0 92 78 0
}
std::cout << std::endl;
}
3. std::replace_copy
원본 유지하며 치환
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> src = {1, 2, 3, 2, 4, 2, 5};
std::vector<int> dst;
// 복사하며 치환 (원본 유지)
std::replace_copy(src.begin(), src.end(),
std::back_inserter(dst), 2, 9);
std::cout << "원본: ";
for (int x : src) {
std::cout << x << " "; // 1 2 3 2 4 2 5 (변경 없음)
}
std::cout << std::endl;
std::cout << "복사본: ";
for (int x : dst) {
std::cout << x << " "; // 1 9 3 9 4 9 5
}
std::cout << std::endl;
}
std::replace_copy_if
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> src = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> dst;
// 짝수를 0으로 치환하며 복사
std::replace_copy_if(src.begin(), src.end(),
std::back_inserter(dst),
{ return x % 2 == 0; }, 0);
std::cout << "원본: ";
for (int x : src) {
std::cout << x << " "; // 1 2 3 4 5 6 7 8 9 10
}
std::cout << std::endl;
std::cout << "복사본: ";
for (int x : dst) {
std::cout << x << " "; // 1 0 3 0 5 0 7 0 9 0
}
std::cout << std::endl;
}
4. 실전 예제
예제 1: 데이터 정제
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
// 센서 데이터 (오류 값 -1 포함)
std::vector<int> sensorData = {23, -1, 25, 24, -1, 26, 23, -1};
// 오류 값을 평균값으로 치환
int validSum = 0;
int validCount = 0;
for (int value : sensorData) {
if (value != -1) {
validSum += value;
validCount++;
}
}
int average = validSum / validCount;
std::replace(sensorData.begin(), sensorData.end(), -1, average);
std::cout << "정제된 데이터: ";
for (int value : sensorData) {
std::cout << value << " "; // 23 24 25 24 24 26 23 24
}
std::cout << std::endl;
}
예제 2: 텍스트 처리
#include <iostream>
#include <algorithm>
#include <string>
int main() {
std::string text = "Hello, World! How are you?";
// 구두점을 공백으로
std::replace_if(text.begin(), text.end(),
{ return c == ',' || c == '!' || c == '?'; }, ' ');
std::cout << text << std::endl;
// Hello World How are you
// 연속된 공백 제거는 unique 사용
}
예제 3: 범위 치환
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> grades = {95, 45, 78, 34, 89, 67, 23, 91};
// 60점 미만을 60점으로 상향 (최소 점수 보장)
std::replace_if(grades.begin(), grades.end(),
{ return grade < 60; }, 60);
std::cout << "조정된 점수: ";
for (int grade : grades) {
std::cout << grade << " "; // 95 60 78 60 89 67 60 91
}
std::cout << std::endl;
}
5. 치환 알고리즘 정리
알고리즘 비교
| 알고리즘 | 원본 수정 | 조건 | 시간 복잡도 |
|---|---|---|---|
replace | ✅ | 값 비교 | O(n) |
replace_if | ✅ | Predicate | O(n) |
replace_copy | ❌ | 값 비교 | O(n) |
replace_copy_if | ❌ | Predicate | O(n) |
함수 시그니처
// 값 치환 (원본 수정)
template<class ForwardIt, class T>
void replace(ForwardIt first, ForwardIt last,
const T& old_value, const T& new_value);
// 조건 치환 (원본 수정)
template<class ForwardIt, class UnaryPredicate, class T>
void replace_if(ForwardIt first, ForwardIt last,
UnaryPredicate pred, const T& new_value);
// 복사하며 값 치환
template<class InputIt, class OutputIt, class T>
OutputIt replace_copy(InputIt first, InputIt last, OutputIt d_first,
const T& old_value, const T& new_value);
// 복사하며 조건 치환
template<class InputIt, class OutputIt, class UnaryPredicate, class T>
OutputIt replace_copy_if(InputIt first, InputIt last, OutputIt d_first,
UnaryPredicate pred, const T& new_value);
6. 자주 발생하는 문제
문제 1: 원본 수정 vs 복사
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 2, 4};
// replace: 원본 수정
std::replace(v.begin(), v.end(), 2, 9);
std::cout << "원본 수정: ";
for (int x : v) {
std::cout << x << " "; // 1 9 3 9 4
}
std::cout << std::endl;
// ✅ 원본 유지하려면 replace_copy
std::vector<int> v2 = {1, 2, 3, 2, 4};
std::vector<int> dst;
std::replace_copy(v2.begin(), v2.end(),
std::back_inserter(dst), 2, 9);
std::cout << "원본: ";
for (int x : v2) {
std::cout << x << " "; // 1 2 3 2 4 (변경 없음)
}
std::cout << std::endl;
}
문제 2: 여러 값 치환
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// ❌ 비효율적: 여러 번 순회
std::replace(v.begin(), v.end(), 2, 0);
std::replace(v.begin(), v.end(), 4, 0);
// ✅ 효율적: 한 번 순회
std::vector<int> v2 = {1, 2, 3, 4, 5};
std::replace_if(v2.begin(), v2.end(),
{ return x == 2 || x == 4; }, 0);
for (int x : v2) {
std::cout << x << " "; // 1 0 3 0 5
}
std::cout << std::endl;
}
문제 3: std::string::replace와 혼동
#include <iostream>
#include <algorithm>
#include <string>
int main() {
std::string text = "hello world";
// std::replace (알고리즘): 문자 하나씩 치환
std::replace(text.begin(), text.end(), 'l', 'L');
std::cout << text << std::endl; // heLLo worLd
// std::string::replace (멤버 함수): 부분 문자열 치환
text.replace(0, 5, "HELLO");
std::cout << text << std::endl; // HELLO worLd
// 다른 기능!
}
문제 4: 반복자 무효화
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// ✅ replace는 반복자를 무효화하지 않음
auto it = v.begin();
std::replace(v.begin(), v.end(), 3, 99);
std::cout << *it << std::endl; // 1 (여전히 유효)
// 주의: 크기 변경 연산(insert, erase)은 반복자 무효화
}
7. transform vs replace
차이점
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = {1, 2, 3, 4, 5};
// replace: 특정 값만 치환
std::replace(v1.begin(), v1.end(), 2, 9);
std::cout << "replace: ";
for (int x : v1) {
std::cout << x << " "; // 1 9 3 4 5
}
std::cout << std::endl;
// transform: 모든 요소 변환
std::transform(v2.begin(), v2.end(), v2.begin(),
{ return x * 2; });
std::cout << "transform: ";
for (int x : v2) {
std::cout << x << " "; // 2 4 6 8 10
}
std::cout << std::endl;
}
언제 무엇을 사용할까
| 상황 | 사용할 알고리즘 |
|---|---|
| 특정 값을 다른 값으로 | replace |
| 조건에 맞는 값만 치환 | replace_if |
| 모든 요소를 변환 | transform |
| 원본 유지하며 치환 | replace_copy |
8. 실전 예제: 데이터 전처리
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
class DataPreprocessor {
public:
// 이상치 제거 (평균으로 치환)
static void replaceOutliers(std::vector<double>& data, double threshold) {
double mean = std::accumulate(data.begin(), data.end(), 0.0) / data.size();
std::replace_if(data.begin(), data.end(),
[mean, threshold](double x) {
return std::abs(x - mean) > threshold;
}, mean);
}
// 음수를 0으로
static void replaceNegatives(std::vector<int>& data) {
std::replace_if(data.begin(), data.end(),
{ return x < 0; }, 0);
}
// 결측치 처리
static void replaceMissing(std::vector<int>& data, int missingValue, int replacement) {
std::replace(data.begin(), data.end(), missingValue, replacement);
}
};
int main() {
// 센서 데이터
std::vector<double> temperatures = {23.5, 24.0, 100.0, 23.8, 24.2, -50.0, 24.5};
std::cout << "원본: ";
for (double t : temperatures) {
std::cout << t << " ";
}
std::cout << std::endl;
// 이상치 제거 (평균에서 50 이상 차이)
DataPreprocessor::replaceOutliers(temperatures, 50.0);
std::cout << "전처리 후: ";
for (double t : temperatures) {
std::cout << t << " ";
}
std::cout << std::endl;
}
정리
핵심 요약
- replace: 특정 값을 다른 값으로 치환 (원본 수정)
- replace_if: 조건에 맞는 값 치환
- replace_copy: 복사하며 치환 (원본 유지)
- replace_copy_if: 복사하며 조건 치환
- 시간 복잡도: 모두 O(n)
실전 팁
-
선택 기준
- 원본 수정 가능:
replace,replace_if - 원본 유지:
replace_copy,replace_copy_if - 특정 값:
replace,replace_copy - 조건 기반:
replace_if,replace_copy_if
- 원본 수정 가능:
-
성능 최적화
- 여러 값 치환 시
replace_if사용 - 단일 패스로 처리
- 불필요한 복사 피하기
- 여러 값 치환 시
-
주의사항
std::string::replace와 다름- 반복자는 유효 (크기 변경 없음)
- Predicate는 부작용 없어야 함
다음 단계
- C++ Algorithm Reverse
- C++ Algorithm Remove
- C++ Algorithm Transform
관련 글
- C++ STL 알고리즘 기초 완벽 가이드 | sort·find