메뉴 닫기

STL equal_range 함수 완전 정복, lower_bound와 upper_bound를 대체하는 깔끔한 방법

STL equal_range 함수 완전 정복, lower_bound와 upper_bound를 대체하는 깔끔한 방법

🚀 복잡한 코드 없이 정렬된 범위를 한 번에 찾는 법, equal_range로 해결하세요

C++ STL에서 이진 탐색 계열 함수는 정말 유용하지만, 그중에서도 equal_range는 아는 사람만 제대로 쓰는 고급 함수입니다.
정렬된 컨테이너에서 특정 값이 처음 등장하는 위치와 마지막 위치를 동시에 알고 싶을 때, 보통 lower_boundupper_bound를 각각 호출하는 방식이 일반적이죠.
하지만 equal_range를 사용하면 단 한 줄로 이 두 가지 정보를 한꺼번에 얻을 수 있어 코드가 훨씬 간결해집니다.

이번 글에서는 equal_range 함수의 기본 개념부터 실전 사용법, lower_bound·upper_bound와의 차이점까지 모두 설명드릴게요.
처음 접하시는 분도 쉽게 이해할 수 있도록 다양한 예제와 함께 준비했으니 끝까지 읽어보시면 분명 실무에도 도움이 될 겁니다.







📌 equal_range란 어떤 함수인가요?

C++ STL의 equal_range 함수는 정렬된 컨테이너에서 특정 값의 시작 위치와 종료 위치를 한 번에 반환해주는 함수입니다.
이 함수는 내부적으로 lower_boundupper_bound를 동시에 수행하기 때문에, 결과적으로 두 개의 반복자 쌍(pair)을 반환합니다.

반환되는 쌍의 첫 번째는 찾고자 하는 값이 처음 등장하는 위치,
두 번째는 그 값보다 큰 첫 위치를 가리킵니다.
즉, equal_range는 특정 값과 동일한 원소가 몇 개나 존재하는지를 계산할 때 매우 유용합니다.

CODE BLOCK
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {1, 2, 2, 2, 3, 4, 5};
    auto range = std::equal_range(v.begin(), v.end(), 2);
    
    std::cout << "시작 인덱스: " << (range.first - v.begin()) << '\n';
    std::cout << "종료 인덱스: " << (range.second - v.begin()) << '\n';
    return 0;
}

💎 핵심 포인트:
equal_range는 이진 탐색 기반이므로 시간복잡도는 O(log N)이며, 정렬된 컨테이너에서만 사용 가능합니다.

이처럼 equal_range를 사용하면 같은 값이 연속으로 여러 개 들어 있는 경우에도 해당 구간을 정확히 파악할 수 있어, 데이터 처리나 통계 분석, 중복 제거 등 다양한 분야에서 활용됩니다.


📌 lower_bound와 upper_bound와의 차이

equal_range를 이해하려면 먼저 lower_boundupper_bound의 동작 방식부터 알아두는 것이 좋습니다.
두 함수 모두 이진 탐색 기반으로 작동하며, 정렬된 컨테이너에서 특정 값의 위치를 찾는 데 사용됩니다.

🔍 lower_bound

찾고자 하는 값 이상이 처음으로 나오는 위치를 가리킵니다.
존재하지 않으면 범위 끝 반복자를 반환합니다.

🔍 upper_bound

찾고자 하는 값 초과가 처음 등장하는 위치를 가리킵니다.
즉, 같은 값이 여러 개 있다면 마지막 값 바로 뒤를 반환합니다.

CODE BLOCK
std::vector<int> v = {1, 2, 2, 2, 3, 4, 5};
auto lb = std::lower_bound(v.begin(), v.end(), 2); // 첫 2의 위치
auto ub = std::upper_bound(v.begin(), v.end(), 2); // 마지막 2 바로 다음 위치

💡 TIP: 동일한 값이 여러 개 있는 경우, upper_bound – lower_bound를 통해 값의 개수를 손쉽게 구할 수 있습니다.

하지만 이렇게 두 개의 함수를 각각 호출해야 하니 코드가 다소 길어지고,
std::pair를 직접 구성해야 할 수도 있습니다.
이러한 불편함을 해소하기 위해 equal_range가 도입된 것이죠.







📌 equal_range의 반환값과 사용법

equal_range는 std::pair 객체를 반환합니다.
이 pair는 첫 반복자: lower_bound, 두 번째 반복자: upper_bound를 의미합니다.
결과적으로 equal_range는 특정 값이 등장하는 시작과 끝 범위를 동시에 알려주는 셈이죠.

사용법은 다음과 같습니다.

CODE BLOCK
std::vector<int> vec = {10, 20, 20, 20, 30, 40};
auto result = std::equal_range(vec.begin(), vec.end(), 20);

// result.first는 20이 처음 등장하는 위치
// result.second는 20보다 큰 값이 처음 등장하는 위치

equal_range를 활용하면 반복자의 범위를 기반으로 다음과 같은 작업을 할 수 있습니다:

  • 🔍중복된 원소의 개수를 계산하기
  • 🧹특정 값만을 삭제하거나 필터링하는 데 활용
  • 📐구간 반복을 통해 범위 기반 처리 수행

특히 for (auto it = range.first; it != range.second; ++it)와 같은 반복문은 equal_range의 대표적인 활용 예입니다.
이를 통해 원하는 값에 해당하는 요소만 골라 처리할 수 있죠.

💎 핵심 포인트:
equal_range는 lower_bound와 upper_bound를 한 번에 실행하기 때문에 성능과 가독성 모두 잡을 수 있는 효율적인 선택입니다.


📌 실전 예제: 벡터에서 값의 범위 찾기

equal_range를 실전 코드에서 어떻게 사용하는지 궁금하시죠?
이번엔 가장 많이 사용되는 std::vector를 기준으로, 특정 값의 시작과 끝 위치를 찾는 예제를 살펴볼게요.

예를 들어, 값 50이 벡터에 몇 번 들어 있는지 알고 싶을 때, equal_range를 사용하면 아주 간결하게 해결됩니다.

CODE BLOCK
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> scores = {30, 40, 50, 50, 50, 60, 70};

    auto range = std::equal_range(scores.begin(), scores.end(), 50);

    int count = range.second - range.first;

    std::cout << "50의 개수: " << count << std::endl;
    return 0;
}

위 코드에서 equal_range는 50이 처음 나오는 위치50이 끝나는 다음 위치를 반환합니다.
그리고 그 두 반복자 간의 차를 이용해 값의 개수를 구하죠.

💡 TIP: 만약 해당 값이 벡터에 없다면 range.first와 range.second는 같은 위치를 가리키게 됩니다. 이로 인해 count는 0이 되며, “값 없음”을 자연스럽게 확인할 수 있어요.

equal_range는 이렇게 중복된 값 처리에 최적화되어 있으며, 이진 탐색 기반이라 속도까지 빠르기 때문에 대용량 데이터에서도 매우 효과적입니다.







📌 equal_range 사용 시 주의할 점

equal_range는 매우 유용한 함수이지만, 사용 시 몇 가지 중요한 조건과 주의사항을 알아두어야 해요.
특히 이 함수는 정렬된 컨테이너에만 사용할 수 있다는 점을 반드시 기억해야 합니다.

⚠️ 주의: equal_range는 내부적으로 이진 탐색 알고리즘을 기반으로 하기 때문에, 컨테이너가 오름차순 정렬되어 있지 않으면 정확하지 않은 결과를 반환합니다.

🧭 정렬이 되어 있는가?

equal_range를 호출하기 전에 반드시 std::sort()로 정렬을 먼저 수행해야 해요.
정렬되지 않은 벡터나 리스트에서 equal_range를 사용하면, 의도와 전혀 다른 결과를 낼 수 있어 디버깅에 시간을 많이 소모하게 됩니다.

📌 동일한 비교 기준 사용

equal_range에 비교 함수(Compare)를 전달하는 경우, 정렬할 때 사용한 비교 기준과 반드시 일치해야 해요.
그렇지 않으면 비교 불일치로 인해 예외 없이 잘못된 결과가 발생합니다.

💎 핵심 포인트:
equal_range를 사용할 땐 항상 정렬 여부와 비교 기준의 일관성을 먼저 확인하세요. 이는 알고리즘의 정확도와 신뢰성을 좌우하는 중요한 요소입니다.

이 외에도 iterator 범위가 올바르게 설정되어야 하며, map과 set 같은 연관 컨테이너에서도 사용 가능하지만 multimap, multiset과 같이 중복을 허용하는 자료형에서 특히 유용하다는 점도 기억해두면 좋아요.


❓ 자주 묻는 질문 (FAQ)

equal_range는 정렬되지 않은 벡터에서도 사용할 수 있나요?
아닙니다. 반드시 정렬된 컨테이너에서만 올바른 결과를 보장합니다. 정렬되지 않은 경우 결과가 잘못 나올 수 있습니다.
equal_range가 반환하는 pair는 어떤 의미인가요?
반환되는 pair는 첫 번째 요소는 lower_bound 위치, 두 번째는 upper_bound 위치를 의미합니다. 즉, 특정 값이 시작하는 위치와 끝나는 위치를 동시에 알려줍니다.
lower_bound, upper_bound보다 equal_range를 써야 하는 이유는 뭔가요?
두 함수를 따로 호출할 필요 없이 한 줄로 간결하게 사용할 수 있어 코드 가독성이 높아지고, 실수 가능성도 줄어듭니다.
equal_range는 어떤 시간 복잡도를 가지나요?
이진 탐색 기반으로 작동하므로 시간 복잡도는 O(log N)입니다. 따라서 대용량 자료에서도 빠른 성능을 기대할 수 있어요.
multimap이나 multiset에서도 equal_range를 쓸 수 있나요?
네, 가능합니다. 오히려 중복된 key가 허용되는 컨테이너에서 equal_range는 아주 유용하게 쓰입니다.
반복문에서 equal_range 결과를 어떻게 활용하나요?
반복자 범위로 순회하며 해당 값에 해당하는 요소만 처리할 수 있습니다. 예: for (it = range.first; it != range.second; ++it)
equal_range에 사용자 정의 비교 함수를 사용할 수 있나요?
네, 가능합니다. 단, 정렬에도 같은 비교 기준을 사용해야 정확한 결과를 보장받을 수 있습니다.
equal_range로 특정 값을 찾았는데 count가 0입니다. 왜 그럴까요?
해당 값이 컨테이너에 존재하지 않는 경우, first와 second가 같은 위치를 가리키게 됩니다. 이 경우 해당 값이 없다는 뜻입니다.


🧠 lower_bound, upper_bound보다 더 똑똑한 선택: equal_range

STL에서 equal_range는 단순히 lower_bound와 upper_bound를 합친 함수가 아닙니다.
복잡한 코드 없이 동일한 기능을 수행할 수 있어, 가독성과 효율성 모두를 챙길 수 있는 스마트한 도구예요.
정렬된 컨테이너에서 특정 값의 시작과 끝 위치를 정확히 알고 싶다면, 이제는 두 개의 함수를 따로 호출할 필요 없이 equal_range 하나로 해결하세요.

또한 중복 값 처리나 구간 반복 처리 등 실전 개발에서도 자주 활용되는 함수인 만큼, 사용법과 주의사항을 잘 익혀두면 여러분의 코드 퀄리티가 한층 높아질 거예요.
특히 multiset, multimap과 같은 연관 컨테이너와도 찰떡궁합이니 꼭 기억해두시고요.

이제 STL 알고리즘의 세계에서 equal_range를 자신 있게 활용해보세요.
깔끔하고 안정적인 코드를 짜는 데 분명 큰 도움이 될 거예요.


🏷️ 관련 태그 : STL알고리즘, equal_range, C++코딩팁, lower_bound, upper_bound, 이진탐색, C++기초, 중복값처리, 정렬기반탐색, stdvector