C++ find·binary_search·lower_bound: 정렬 전제가 깨질 때 생기는 조용한 오류
이 글의 핵심
binary_search 계열은 정렬 전제가 깨지거나 정렬과 검색의 비교자가 다르면 에러 없이 틀린 답을 돌려줍니다. find와 이진 탐색을 고르는 기준, lower_bound·upper_bound·equal_range로 구간을 잡는 법, std::list에서 lower_bound가 느린 이유와 반복자 무효화까지 예제로 정리합니다.
검색 알고리즘이란?
검색 알고리즘 (Search Algorithm) 은 컨테이너에서 특정 값이나 조건을 만족하는 요소를 찾는 STL 알고리즘입니다.
#include <algorithm>
#include <vector>
std::vector<int> v = {1, 2, 3, 4, 5};
// 선형 검색
auto it = std::find(v.begin(), v.end(), 3);
// 이진 검색 (정렬 필요)
bool found = std::binary_search(v.begin(), v.end(), 3);
왜 필요한가?:
- 효율성: 최적화된 검색 알고리즘
- 간결성: 복잡한 로직 간소화
- 정확성: 경계 조건(빈 범위, 마지막 원소)을 이미 검증된 구현이 처리 (단, 반복자 범위가 유효한지는 호출자 책임)
- 유연성: 다양한 검색 방법 지원
STL이 find와 binary_search 계열을 굳이 나눠 놓은 이유는 단순히 “취향”이 아니라 알고리즘 선택 자체가 정확성 문제이기 때문입니다. std::find는 컨테이너가 정렬되어 있든 아니든 항상 올바르게 동작합니다. 대신 최악의 경우 컨테이너 전체를 순회해야 하므로 O(n)입니다. 반면 binary_search, lower_bound, upper_bound, equal_range는 대상 범위가 해당 비교 기준으로 이미 정렬되어 있다는 전제를 깔고 동작합니다. 이 전제가 깨지면 크래시가 나거나 예외가 던져지는 것이 아니라 — 그냥 틀린 답을 아무 경고 없이 반환합니다. 존재하는 값을 못 찾았다고 반환하거나, 반대로 없는 값이 있다고 반환할 수도 있습니다. 이런 종류의 버그는 코드 리뷰에서도, 단위 테스트에서도 잡히지 않고 프로덕션에서 특정 입력 순서로만 재현되는 경우가 많아서 디버깅 난이도가 꽤 높습니다.
// ❌ 수동 검색: 복잡
bool found = false;
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] == 3) {
found = true;
break;
}
}
// ✅ find: 간결
auto it = std::find(v.begin(), v.end(), 3);
bool found = (it != v.end());
검색 알고리즘 종류:
| 알고리즘 | 시간 복잡도 | 정렬 필요 | 설명 |
|---|---|---|---|
find | O(n) | ❌ | 값 검색 |
find_if | O(n) | ❌ | 조건 검색 |
binary_search | O(log n) | ✅ | 이진 검색 (존재 여부) |
lower_bound | O(log n) | ✅ | >= value 첫 위치 |
upper_bound | O(log n) | ✅ | > value 첫 위치 |
equal_range | O(log n) | ✅ | [lower, upper) 범위 |
std::vector<int> v = {1, 2, 3, 4, 5};
// 선형 검색: O(n)
auto it = std::find(v.begin(), v.end(), 3);
// 이진 검색: O(log n) (정렬 필요)
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), 3);
검색 알고리즘 선택 가이드:
flowchart TD
A["검색 시작"]
B{"정렬됨?"}
C{"값 검색?"}
D[find]
E[find_if]
F{"존재 확인?"}
G[binary_search]
H{"삽입 위치?"}
I[lower_bound]
J[equal_range]
A --> B
B -->|No| C
C -->|Yes| D
C -->|No| E
B -->|Yes| F
F -->|Yes| G
F -->|No| H
H -->|Yes| I
H -->|No| J
정렬 전제가 깨지면 왜 위험한가
binary_search, lower_bound, upper_bound, equal_range는 모두 내부적으로 “가운데 원소를 보고 왼쪽/오른쪽 중 어느 쪽에 답이 있을지 결정”하는 방식으로 동작합니다. 이 결정은 범위가 정렬되어 있다는 가정 위에서만 성립합니다. 정렬이 깨진 범위에 이 함수들을 쓰면 알고리즘은 여전히 “정상적으로” 실행됩니다 — 크래시도, 예외도, 어떤 진단 메시지도 없습니다. 다만 절반씩 잘라나가는 과정에서 실제로는 존재하는 값이 있는 쪽을 건너뛰어 버리기 때문에, 결과적으로 엉뚱한 위치를 반환하거나 존재하는 값을 못 찾았다고 답합니다. 이것이 std::find의 O(n) 순회와 근본적으로 다른 지점입니다. find는 정렬 여부와 무관하게 항상 정답을 보장하지만, 이진 탐색 계열은 정렬이 전제 조건이자 계약(contract)이고 그 계약이 깨지면 조용히 실패합니다.
이 문제가 가장 흔히 생기는 경로는 리팩터링입니다. 처음에는 아이디(id) 오름차순으로 정렬한 벡터에 lower_bound로 검색하다가, “최근 항목을 먼저 보여 달라”는 요구로 정렬 기준을 내림차순으로 바꾸면서 검색 쪽 lower_bound 호출은 기본 비교자(operator<, 오름차순 가정) 그대로 남겨 두는 식입니다. 컴파일은 당연히 통과하고, 테스트 데이터가 작으면 우연히 맞는 답이 나와 초기 검증에서도 걸러지지 않습니다. 증상은 “존재하는 항목을 검색이 없다고 답한다”로 나타나는데, 검색 코드 자체에는 잘못이 없어 보이기 때문에 원인을 찾기까지 오래 걸리는 부류의 버그입니다. 정렬에 쓴 비교자와 검색에 쓴 비교자가 일치하는지는 컴파일러가 검사해주지 않으므로, 정렬 기준을 바꿀 때는 그 컨테이너를 대상으로 하는 모든 이진 탐색 호출을 같이 점검하는 습관이 필요합니다.
또 하나 흔한 함정은 “이 데이터는 거의 정렬되어 있으니 괜찮겠지”라는 가정입니다. 외부 API에서 받아온 데이터, 여러 스레드가 병합해 넣은 데이터, 혹은 삽입 후 재정렬을 깜빡한 데이터는 대부분의 구간은 정렬 순서를 지키지만 일부 구간만 순서가 어긋나 있는 경우가 흔합니다. 이런 상태에서 binary_search를 돌리면 대부분의 검색은 우연히 맞는 답을 내지만, 순서가 어긋난 구간 근처의 값을 찾을 때만 간헐적으로 틀린 결과가 나옵니다. 이런 버그는 “가끔씩만” 실패하는 검색으로 드러나 재현이 어렵고, 원인을 따라가 보면 배치 작업이 데이터를 뒤에 추가(append)만 하고 재정렬을 하지 않은 경우가 많습니다. 디버그 빌드에서 assert(std::is_sorted(v.begin(), v.end()))를 검색 직전에 넣어두는 것만으로도 이런 문제를 훨씬 빨리 잡을 수 있습니다. is_sorted는 O(n)이라 항상 켜두기엔 부담스럽지만, 디버그/테스트 빌드에서는 거의 공짜에 가까운 안전장치입니다.
find
#include <algorithm>
std::vector<int> v = {1, 2, 3, 4, 5};
// find: 값 검색
auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end()) {
std::cout << "찾음: " << *it << std::endl;
}
// find_if: 조건 검색
auto it2 = std::find_if(v.begin(), v.end(), [](int x) {
return x > 3;
});
find는 찾지 못하면 예외를 던지거나 -1 같은 값을 돌려주지 않고 끝 반복자(last)를 반환합니다. 그래서 결과를 쓰기 전에 항상 it != v.end()를 확인해야 하고, 이것을 빼먹고 *it를 역참조하면 범위 밖 메모리를 읽는 정의되지 않은 동작이 됩니다. 부분 범위를 검색했다면 비교 대상도 v.end()가 아니라 그 범위의 끝 반복자여야 한다는 점도 자주 틀립니다. C++20의 std::ranges::find(v, 3)을 쓰면 반복자 쌍을 따로 넘기지 않아 범위를 잘못 넘기는 실수가 줄고, 프로젝션(std::ranges::find(people, "Bob", &Person::name))으로 멤버 기준 검색도 람다 없이 쓸 수 있습니다.
실전 예시
예시 1: 구조체 검색
#include <algorithm>
#include <vector>
#include <string>
struct Person {
std::string name;
int age;
};
int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
// 이름으로 검색
auto it = std::find_if(people.begin(), people.end(),
[](const Person& p) { return p.name == "Bob"; });
if (it != people.end()) {
std::cout << "찾음: " << it->name << " (" << it->age << ")" << std::endl;
}
}
예시 2: binary_search
#include <algorithm>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 이진 검색 (정렬 필요)
bool found = std::binary_search(v.begin(), v.end(), 5);
if (found) {
std::cout << "5 존재" << std::endl;
}
}
예시 3: lower_bound & upper_bound
#include <algorithm>
int main() {
std::vector<int> v = {1, 2, 2, 2, 3, 4, 5};
// lower_bound: >= value
auto lower = std::lower_bound(v.begin(), v.end(), 2);
std::cout << "lower: " << std::distance(v.begin(), lower) << std::endl; // 1
// upper_bound: > value
auto upper = std::upper_bound(v.begin(), v.end(), 2);
std::cout << "upper: " << std::distance(v.begin(), upper) << std::endl; // 4
// equal_range: [lower, upper)
auto [first, last] = std::equal_range(v.begin(), v.end(), 2);
std::cout << "개수: " << std::distance(first, last) << std::endl; // 3
}
예시 4: 삽입 위치
#include <algorithm>
int main() {
std::vector<int> v = {1, 3, 5, 7, 9};
// 정렬 유지하며 삽입할 위치
int value = 6;
auto pos = std::lower_bound(v.begin(), v.end(), value);
v.insert(pos, value);
for (int x : v) {
std::cout << x << " "; // 1 3 5 6 7 9
}
}
lower_bound는 “값을 넣어도 정렬이 유지되는 가장 앞 위치”를, upper_bound는 “가장 뒤 위치”를 돌려줍니다. 같은 값이 이미 있을 때 lower_bound 위치에 넣으면 기존 값들 앞에, upper_bound 위치에 넣으면 뒤에 들어갑니다. 같은 키를 가진 레코드의 입력 순서를 유지하고 싶다면 upper_bound를 써야 합니다. 이 삽입 방식은 위치 찾기는 O(log n)이지만 vector::insert가 뒤의 원소를 모두 한 칸씩 밀어야 해서 삽입 자체는 O(n)입니다. 원소를 하나씩 수천·수만 번 넣는다면 전체가 O(n²)이 되므로, 한꺼번에 모아 넣고 마지막에 한 번 sort하거나 std::set/std::multiset을 쓰는 편이 낫습니다.
컨테이너 종류에 따른 성능 함정
lower_bound의 시간 복잡도가 O(log n)이라는 설명은 정확히는 “비교 연산 횟수” 기준입니다. std::vector나 배열처럼 랜덤 액세스 반복자(random-access iterator)를 지원하는 컨테이너에서는 중간 원소로 한 번에 점프할 수 있으므로 비교 횟수와 실제 실행 시간이 둘 다 O(log n)입니다. 하지만 std::list처럼 양방향 반복자(bidirectional iterator)만 지원하는 컨테이너에서는 “중간으로 점프”하는 연산 자체가 존재하지 않습니다. std::advance가 내부적으로 반복자를 한 칸씩 옮기는 식으로 흉내를 내야 하므로, 비교 횟수는 O(log n)이어도 반복자 이동 횟수가 O(n)이 되어 전체 실행 시간은 사실상 선형 탐색과 다를 바 없어집니다. 즉 “정렬되어 있으니까 lower_bound를 쓰면 무조건 빠르다”는 생각은 컨테이너 종류를 확인하지 않으면 틀릴 수 있습니다. std::list에 대해 정렬된 상태에서 값을 자주 찾아야 한다면, 애초에 std::set이나 std::map처럼 검색에 최적화된 연관 컨테이너를 쓰는 편이 낫습니다.
#include <list>
#include <set>
#include <algorithm>
std::list<int> lst = {1, 2, 3, 4, 5};
// 컴파일은 되지만 std::list는 랜덤 액세스 반복자가 아니므로
// 비교는 O(log n)이어도 반복자 이동은 O(n) — 사실상 선형 탐색과 유사
auto it = std::lower_bound(lst.begin(), lst.end(), 3);
// 검색이 잦다면 애초에 정렬된 연관 컨테이너를 쓰는 것이 낫다
std::set<int> s = {1, 2, 3, 4, 5};
auto sit = s.lower_bound(3); // 트리 구조 자체가 O(log n) 탐색을 보장
비교자(comparator) 일관성
sort를 커스텀 비교자로 했다면 같은 범위를 검색할 때도 반드시 동일한 비교자를 넘겨야 합니다. binary_search/lower_bound/upper_bound/equal_range는 기본적으로 operator<를 사용하는데, 정렬에 쓴 비교자와 검색에 쓴 비교자가 다르면 “정렬 기준”과 “탐색 기준”이 어긋나면서 위에서 설명한 정렬 전제 위반과 똑같은 문제가 발생합니다. 컴파일러는 두 비교자가 논리적으로 같은 순서를 정의하는지 검사해줄 방법이 없으므로, 이 불일치는 런타임에만 드러나는 조용한 버그가 됩니다.
struct Item {
int priority;
std::string name;
};
// 내림차순으로 정렬
std::vector<Item> items = {{3, "a"}, {1, "b"}, {2, "c"}};
std::sort(items.begin(), items.end(),
[](const Item& a, const Item& b) { return a.priority > b.priority; });
// ❌ 검색에는 기본 비교자(오름차순 가정)를 그대로 사용 — 비교자 불일치
auto it = std::lower_bound(items.begin(), items.end(), Item{2, ""},
[](const Item& a, const Item& b) { return a.priority < b.priority; });
// items는 내림차순인데 검색은 오름차순을 가정하므로 결과가 틀릴 수 있음
// ✅ 정렬에 쓴 것과 동일한 비교자를 검색에도 사용
auto correctIt = std::lower_bound(items.begin(), items.end(), Item{2, ""},
[](const Item& a, const Item& b) { return a.priority > b.priority; });
equal_range가 lower_bound와 upper_bound를 각각 따로 호출하는 것보다 나은 이유도 여기서 짚을 만합니다. 두 함수를 따로 호출하면 각각 처음부터 이진 탐색을 시작하므로 비교 범위가 일부 겹칩니다. equal_range는 한 번의 탐색 과정에서 값과 같은 구간의 경계를 동시에 좁혀 나가기 때문에, 두 번 따로 호출하는 것보다 총 비교 횟수가 적습니다. 점근적 복잡도(O(log n))는 동일하지만, 상수 배 차이가 있어서 같은 키의 구간을 자주 조회하는 코드라면 equal_range 하나로 통일하는 편이 실질적으로 더 빠릅니다.
검색 알고리즘
// 선형 검색
std::find(begin, end, value)
std::find_if(begin, end, pred)
std::find_if_not(begin, end, pred)
// 이진 검색 (정렬 필요)
std::binary_search(begin, end, value)
std::lower_bound(begin, end, value)
std::upper_bound(begin, end, value)
std::equal_range(begin, end, value)
// 인접 검색
std::adjacent_find(begin, end)
// 부분 검색
std::search(begin1, end1, begin2, end2)
자주 발생하는 문제
문제 1: 정렬 여부
std::vector<int> v = {3, 1, 4, 1, 5};
// ❌ 정렬 안 됨
// bool found = std::binary_search(v.begin(), v.end(), 3); // 정의되지 않은 동작
// ✅ 정렬 후 이진 검색
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), 3);
문제 2: 반복자 무효화
std::vector<int> v = {1, 2, 3, 4, 5};
auto it = std::find(v.begin(), v.end(), 3);
// ❌ 삽입 후 반복자 무효화
v.push_back(6);
// it 무효화 가능
// ✅ 인덱스 사용 (push_back 전에 인덱스를 저장해 둠)
auto index = std::distance(v.begin(), it);
v.push_back(6);
auto newIt = v.begin() + index;
vector::push_back은 용량(capacity)을 넘으면 더 큰 메모리를 새로 할당하고 원소를 옮긴 뒤 옛 메모리를 해제합니다. 그 순간 이전에 얻은 모든 반복자·포인터·참조가 해제된 메모리를 가리키게 됩니다. 용량이 남아 있으면 재할당이 없어 우연히 동작하기 때문에, 테스트에서는 멀쩡하다가 데이터가 늘어나면 가끔 크래시가 나는 형태로 드러납니다. 인덱스는 재할당과 무관하므로 안전하고, 삽입 전에 reserve로 충분한 용량을 확보해 두는 방법도 있습니다. 디버그 빌드에서는 MSVC의 _ITERATOR_DEBUG_LEVEL이나 libstdc++의 -D_GLIBCXX_DEBUG가 무효화된 반복자 사용을 즉시 잡아 줍니다.
문제 3: 성능
// find: O(n)
auto it = std::find(v.begin(), v.end(), value);
// binary_search: O(log n) (정렬 필요)
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), value);
// 여러 번 검색 시 정렬 후 이진 검색
정렬은 O(n log n)이므로 검색을 한두 번만 한다면 정렬하는 비용이 선형 검색보다 큽니다. 대략 “검색 횟수가 log n보다 훨씬 많을 때” 정렬 후 이진 검색이 이득입니다. 또 원소가 수십 개 수준의 작은 벡터라면, 연속 메모리를 앞에서부터 읽는 선형 검색이 분기 예측과 캐시 측면에서 유리해 이진 검색보다 빠른 경우도 흔합니다. 검색이 잦고 데이터가 계속 바뀐다면 정렬된 vector를 유지하기보다 std::unordered_set(평균 O(1))이나 std::set(O(log n), 순서 유지)이 맞는 도구입니다.
문제 4: 범위
std::vector<int> v = {1, 2, 3, 4, 5};
// ✅ 전체 검색
auto it = std::find(v.begin(), v.end(), 3);
// ✅ 부분 검색
auto it2 = std::find(v.begin() + 1, v.begin() + 4, 3);
활용 패턴
// 1. 선형 검색
auto it = std::find(v.begin(), v.end(), value);
// 2. 조건 검색
auto it = std::find_if(v.begin(), v.end(), pred);
// 3. 이진 검색
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), value);
// 4. 삽입 위치
auto pos = std::lower_bound(v.begin(), v.end(), value);
v.insert(pos, value);
실무 패턴
패턴 1: 조건부 검색
#include <algorithm>
#include <vector>
struct User {
std::string name;
int age;
bool active;
};
std::vector<User>::iterator findActiveUser(
std::vector<User>& users,
const std::string& name
) {
return std::find_if(users.begin(), users.end(),
[&name](const User& u) {
return u.active && u.name == name;
});
}
// 사용
std::vector<User> users = {
{"Alice", 25, true},
{"Bob", 30, false},
{"Charlie", 35, true}
};
auto it = findActiveUser(users, "Charlie");
if (it != users.end()) {
std::cout << "활성 사용자 찾음: " << it->name << '\n';
}
패턴 2: 범위 검색
#include <algorithm>
#include <vector>
std::pair<int, int> findRange(
const std::vector<int>& sorted,
int minVal,
int maxVal
) {
auto lower = std::lower_bound(sorted.begin(), sorted.end(), minVal);
auto upper = std::upper_bound(sorted.begin(), sorted.end(), maxVal);
// std::distance는 ptrdiff_t를 반환하므로, 중괄호 초기화에서 int로 줄이려면 명시적 변환이 필요
return {
static_cast<int>(std::distance(sorted.begin(), lower)),
static_cast<int>(std::distance(sorted.begin(), upper))
};
}
// 사용
std::vector<int> scores = {60, 70, 75, 80, 85, 90, 95};
auto [start, end] = findRange(scores, 75, 90);
std::cout << "75-90 범위: ";
for (int i = start; i < end; ++i) {
std::cout << scores[i] << " ";
}
// 출력: 75 80 85 90
닫힌 구간 [75, 90]을 찾을 때 시작은 lower_bound(75), 끝은 upper_bound(90)을 쓰는 조합이 핵심입니다. 끝에 lower_bound(90)을 쓰면 90이 빠진 [75, 90)이 되어, 경계값이 결과에서 빠지는 off-by-one 버그가 됩니다. 원래 코드처럼 std::distance 결과를 중괄호로 int에 담으면 narrowing conversion of ... from 'long int' to 'int' 컴파일 에러가 나므로 위처럼 명시적으로 변환하거나, 반환 타입을 반복자 쌍이나 std::ptrdiff_t로 두는 편이 깔끔합니다. minVal > maxVal이면 upper가 lower보다 앞에 올 수 있으니, 호출하는 쪽에서 start < end를 확인해야 합니다.
패턴 3: 정렬 유지 삽입
#include <algorithm>
#include <vector>
template<typename T>
void insertSorted(std::vector<T>& sorted, const T& value) {
auto pos = std::lower_bound(sorted.begin(), sorted.end(), value);
sorted.insert(pos, value);
}
// 사용
std::vector<int> numbers = {1, 3, 5, 7, 9};
insertSorted(numbers, 6);
insertSorted(numbers, 2);
for (int n : numbers) {
std::cout << n << " ";
}
// 출력: 1 2 3 5 6 7 9
FAQ
Q1: find와 binary_search의 차이는?
A:
- find: 선형 검색, O(n), 정렬 불필요
- binary_search: 이진 검색, O(log n), 정렬 필수
// find: 정렬 불필요
std::vector<int> v = {3, 1, 4, 1, 5};
auto it = std::find(v.begin(), v.end(), 4);
// binary_search: 정렬 필요
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), 4);
Q2: lower_bound와 upper_bound의 차이는?
A:
- lower_bound: >= value 첫 위치
- upper_bound: > value 첫 위치
std::vector<int> v = {1, 2, 2, 2, 3, 4};
auto lower = std::lower_bound(v.begin(), v.end(), 2);
// 인덱스 1 (첫 번째 2)
auto upper = std::upper_bound(v.begin(), v.end(), 2);
// 인덱스 4 (마지막 2 다음)
Q3: binary_search는 위치를 반환하나요?
A: 아니요. 존재 여부만 반환합니다. 위치가 필요하면 lower_bound를 사용하세요.
// binary_search: bool 반환
bool found = std::binary_search(v.begin(), v.end(), 3);
// lower_bound: 반복자 반환
auto it = std::lower_bound(v.begin(), v.end(), 3);
if (it != v.end() && *it == 3) {
std::cout << "위치: " << std::distance(v.begin(), it) << '\n';
}
Q4: 정렬되지 않은 범위에서 이진 검색하면?
A: 정의되지 않은 동작입니다. 반드시 정렬 후 사용하세요.
// ❌ 정렬 안 됨
std::vector<int> v = {3, 1, 4, 1, 5};
bool found = std::binary_search(v.begin(), v.end(), 3); // UB
// ✅ 정렬 후
std::sort(v.begin(), v.end());
bool found = std::binary_search(v.begin(), v.end(), 3);
Q5: find_if의 성능은?
A: O(n) 입니다. 조건을 만족하는 첫 요소를 찾을 때까지 순회합니다.
auto it = std::find_if(v.begin(), v.end(), [](int x) {
return x > 5;
});
Q6: equal_range는 무엇인가요?
A: [lower_bound, upper_bound) 범위를 반환합니다.
std::vector<int> v = {1, 2, 2, 2, 3, 4};
auto [lower, upper] = std::equal_range(v.begin(), v.end(), 2);
std::cout << "2의 개수: " << std::distance(lower, upper) << '\n'; // 3
Q7: 반복자 무효화는?
A: 검색 알고리즘은 반복자를 무효화하지 않습니다. 하지만 삽입/삭제 후에는 주의하세요.
auto it = std::find(v.begin(), v.end(), 3);
// ❌ 삽입 후 반복자 무효화 가능
v.push_back(6);
// it 무효화 가능 (vector의 경우)
// ✅ 인덱스 사용
auto index = std::distance(v.begin(), it);
v.push_back(6);
auto newIt = v.begin() + index;
Q8: 검색 알고리즘 학습 리소스는?
A:
- “Effective STL” by Scott Meyers (Item 43-45)
- “C++ Primer” by Stanley Lippman
- cppreference.com - Search operations
검색 알고리즘은 컨테이너에서 특정 값이나 조건을 만족하는 요소를 찾는 STL 알고리즘이며, 이진 탐색 계열을 쓸 때는 정렬 전제와 비교자 일관성을 함께 지켜야 합니다.
관련 글
- C++ 정렬 알고리즘 구현과 비교: std::sort의 pdqsort, stable_sort, 병렬 정렬, 기수 정렬
- C++ Algorithm Set
- C++ STL Algorithm Functions
- C++ min·max·minmax_element와 std::clamp: 값 비교와 범위 제한
- C++ partition·stable_partition·partition_point: 조건으로 범위 나누기
- STL 알고리즘 기본기
- C++ Algorithm Copy
- C++ Algorithm Count
- C++ Algorithm Generate
- C++ STL 알고리즘 기초: sort·find·transform·accumulate 실전 활용