C++ STL lower_bound upper_bound 완벽 가이드
📌 정렬된 컨테이너에서 원하는 위치를 빠르게 찾는 이진 탐색 함수들
안녕하세요.
C++ STL을 활용할 때 알고 있으면 유용한 함수 중 하나가 바로 lower_bound와 upper_bound입니다.
이 함수들은 모두 정렬된 컨테이너를 대상으로 이진 탐색을 수행해 특정 값이 들어갈 수 있는 위치를 찾아주는 역할을 하죠.
특히 std::vector는 물론이고 std::map, std::set 같은 연관 컨테이너에서도 매우 유용하게 활용됩니다.
이 글에서는 lower_bound와 upper_bound의 개념부터 동작 원리, 활용법, 차이점, 그리고 실전에서 자주 등장하는 활용 예시까지 자세히 다루어 보려고 해요.
단순히 “찾는다”는 개념을 넘어, 어떻게 쓰면 성능도 좋고 코드도 명확해지는지까지 알게 되면 여러분의 STL 활용 능력은 한 단계 더 업그레이드될 겁니다.
📋 목차
🔗 lower_bound와 upper_bound란?
C++ STL에서 제공하는 lower_bound와 upper_bound는 이진 탐색 기반의 함수입니다.
정렬된 컨테이너에서 원하는 위치를 빠르게 찾는 데 사용되며, 특히 검색 범위를 지정하거나 구간을 계산할 때 매우 유용하죠.
두 함수는 모두
💡 TIP: lower_bound는 “값 이상(≥)”, upper_bound는 “값 초과(>)” 위치를 반환합니다. 구간 범위 추출에 딱이죠.
🔍 반환값은 반복자(iterator)
이 함수들은 특정 값을 반환하는 것이 아니라 반복자(iterator)를 반환합니다.
즉, 찾은 위치의 주소를 알려주는 것이며, 이 반복자를 통해 값을 참조하거나 위치 계산에 활용할 수 있어요.
💬 lower_bound는 특정 값 이상이 처음 나오는 위치, upper_bound는 특정 값 초과가 처음 나오는 위치를 가리킵니다.
예를 들어, 정렬된 벡터 {1, 3, 3, 5, 7}에서 lower_bound(3)는 두 번째 요소(3)를, upper_bound(3)는 네 번째 요소(5)를 가리킵니다.
🛠️ 이진 탐색 기반의 동작 원리
lower_bound와 upper_bound는 내부적으로 이진 탐색 알고리즘을 사용하여 요소를 찾습니다.
이진 탐색은 정렬된 데이터를 절반씩 나누며 원하는 값을 찾아가는 방식으로, 매우 빠른 검색 성능을 자랑하죠.
두 함수 모두 시간 복잡도가 O(log N)으로, 데이터가 수만 개 이상이어도 빠르게 위치를 탐색할 수 있습니다.
이는 반복문으로 일일이 탐색하는 것보다 훨씬 효율적이에요.
⚙️ 내부 동작 방식 이해하기
lower_bound는 탐색 중 값이 크거나 같은 위치를 발견하면 그 위치로 범위를 줄입니다.
반면 upper_bound는 값보다 큰 위치를 찾을 때만 이동하므로 기준값보다 큰 최초의 요소를 반환하죠.
이러한 로직은 직접 구현할 수도 있지만, STL의 ready-made 함수를 쓰면 안정성도 높고 코드도 간결해집니다.
vector<int> v = {1, 3, 3, 5, 7};
auto it1 = lower_bound(v.begin(), v.end(), 3); // 첫 번째 3
auto it2 = upper_bound(v.begin(), v.end(), 3); // 3보다 큰 첫 번째 수 (5)
💎 핵심 포인트:
lower_bound는 “값 이상”, upper_bound는 “값 초과” 위치를 반환하며, 둘 다 logN의 빠른 성능을 갖습니다.
⚙️ lower와 upper 차이와 사용 시기
lower_bound와 upper_bound는 비슷해 보이지만, 아주 중요한 차이점이 존재합니다.
바로 기준값을 포함하느냐 포함하지 않느냐인데요.
이 차이를 명확히 이해하고 있어야 올바른 범위 검색이나 조건 필터링이 가능합니다.
📌 반환 기준 비교
| 함수 | 조건 | 설명 |
|---|---|---|
| lower_bound | ≥ target | target 이상의 첫 위치 |
| upper_bound | > target | target 초과의 첫 위치 |
따라서 중복값 포함 여부에 따라 어떤 함수를 써야 할지 달라집니다.
예를 들어 값이 3인 요소를 모두 찾고 싶다면 lower_bound(3)부터 upper_bound(3)까지 범위를 지정하면 되죠.
💬 [lower_bound, upper_bound] 범위는 값이 같은 요소의 구간을 쉽게 추출할 수 있는 강력한 도구입니다.
🔍 사용 예 요약
- 📥값이 존재할 수 있는 최소 위치 찾기 → lower_bound
- 📤값을 넘어서는 첫 위치 찾기 → upper_bound
- 📦중복 요소의 범위 검색 → [lower_bound, upper_bound) 활용
🔌 vector, set, map에서의 활용법
lower_bound와 upper_bound는 다양한 STL 컨테이너에서 사용할 수 있으며,
각 컨테이너의 구조에 따라 활용 방식에도 조금씩 차이가 있습니다.
특히 vector, set, map은 대표적인 대상이죠.
📌 vector에서의 활용
vector는 메모리상 연속된 데이터 구조이기 때문에,
lower_bound와 upper_bound를
vector<int> v = {1, 2, 4, 4, 5, 6};
auto lb = lower_bound(v.begin(), v.end(), 4); // 첫 번째 4
auto ub = upper_bound(v.begin(), v.end(), 4); // 4를 초과하는 첫 번째 요소 (5)
🌳 set에서의 활용
set은 자동으로 정렬되는 컨테이너이며, lower_bound와 upper_bound는 멤버 함수로 제공됩니다.
set<int> s = {1, 3, 5, 7};
auto lb = s.lower_bound(5); // 5를 가리킴
auto ub = s.upper_bound(5); // 7을 가리킴
🗂️ map에서의 활용
map은 key를 기준으로 정렬되며, 역시 멤버 함수 형태의 lower_bound, upper_bound를 제공합니다.
이 경우 반복자가 std::pair<const Key, Value>를 가리키므로, .first와 .second를 통해 접근합니다.
map<string, int> scores = {
{"Alice", 85},
{"Bob", 90},
{"Charlie", 95}
};
auto it = scores.lower_bound("Bob");
cout << it->first << ": " << it->second; // Bob: 90
💎 핵심 포인트:
vector는 <algorithm>에서, set/map은 컨테이너 자체에서 lower_bound와 upper_bound를 지원합니다. 사용 컨테이너에 따라 호출 방식이 다르니 유의하세요.
💡 자주 쓰이는 실전 예제
실제 개발 현장에서 lower_bound와 upper_bound는 매우 다양한 방식으로 사용됩니다.
단순한 검색을 넘어 범위 구간 설정, 중복 요소 필터링, 조건 만족 요소 탐색 등에서 활약하죠.
다음은 실무에 자주 등장하는 패턴 예시입니다.
📍 특정 값의 등장 횟수 세기
vector<int> nums = {1, 2, 3, 3, 3, 4, 5};
int count = upper_bound(nums.begin(), nums.end(), 3)
- lower_bound(nums.begin(), nums.end(), 3);
// count = 3
이 방식은 중복된 값이 몇 개인지 구할 때 매우 유용합니다.
검색된 반복자의 차를 계산하면 곧 개수가 되니까요.
🔎 조건을 만족하는 첫 번째 요소 찾기
vector<int> scores = {60, 65, 70, 80, 90};
auto it = lower_bound(scores.begin(), scores.end(), 75);
if (it != scores.end()) {
cout << "조건을 만족하는 점수: " << *it; // 출력: 80
}
이처럼 특정 기준 이상/초과의 첫 요소를 빠르게 찾을 수 있어,
탐색 효율성이 중요한 문제에서 자주 활용됩니다.
💎 핵심 포인트:
범위를 지정해 특정 구간의 데이터만 처리하거나, 조건을 만족하는 최소 요소를 찾고 싶을 때 lower_bound/upper_bound는 최고의 선택입니다.
❓ 자주 묻는 질문 (FAQ)
lower_bound와 upper_bound는 어떤 차이가 있나요?
정렬되지 않은 컨테이너에서도 사용할 수 있나요?
map이나 set에서도 lower_bound를 사용할 수 있나요?
값이 존재하지 않을 때 반복자는 어떤 값을 가리키나요?
중복된 값을 전부 출력하려면 어떻게 하나요?
직접 이진 탐색을 구현하는 것과 어떤 차이가 있나요?
사용 시 가장 주의해야 할 점은 무엇인가요?
upper_bound – lower_bound는 어떤 의미인가요?
📌 정렬된 데이터에서 빠르고 정확한 위치를 찾고 싶다면
STL에서 제공하는 lower_bound와 upper_bound는 단순한 탐색 함수를 넘어, 정렬된 데이터에서 빠르게 위치를 찾고 유용한 범위 계산을 할 수 있는 핵심 도구입니다.
이진 탐색 기반이라 수천, 수만 개의 데이터에서도 성능 저하 없이 원하는 위치를 정확히 파악할 수 있죠.
특히 vector, set, map 등 다양한 컨테이너에서 각각의 방식에 맞게 활용할 수 있고, 반복자 연산을 통해 구간의 시작과 끝을 명확히 지정할 수 있기 때문에 실무에서도 자주 쓰입니다.
중복된 값을 전부 찾고 싶을 때, 특정 조건 이상의 값을 빠르게 찾고 싶을 때, 또는 어떤 값이 몇 개인지 계산하고 싶을 때 이 함수들을 적절히 활용하면 코드가 훨씬 간결하고 효율적으로 바뀌게 됩니다.
정렬과 탐색이 자주 등장하는 문제에 있어서, 이제는 무작정 반복문을 돌리는 대신 lower_bound와 upper_bound를 먼저 떠올려보는 습관을 들여보세요.
시간과 실수를 줄여주는 최고의 STL 도구가 되어줄 거예요.
🏷️ 관련 태그 : STL탐색, lower_bound, upper_bound, 이진탐색, STL정렬, C++반복자, std::map, std::set, 범위탐색, 알고리즘최적화