포스트

[algorithm] (find, binary_search, lower_bound, upper_bound, distance)

이 글은 제 개인적인 공부를 위해 작성한 글입니다.
틀린 내용이 있을 수 있고, 피드백은 환영합니다.


개요


C++의 algorithm 헤더에는 탐색 관련된 유용한 함수들이 많이 있다.

대표적으로 선형 탐색을 수행하는 find와 비 선형 탐색을 수행하는 binary_search, lower_bound, upper_bound가 있다.


find


find 함수는 선형 탐색($O(n)$)을 수행하여 컨테이너에서 원하는 값을 찾는 함수이다.

first ~ last 범위 내에서 value와 일치하는 첫 번째 원소를 가리키는 반복자를 반환한다.

만약 일치하는 원소를 찾지 못하면 last 반복자를 반환한다.

1
2
3
// 함수 원형
template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& val);
1
2
3
4
5
6
7
8
9
// 벡터에서 정수 찾는 예시
vector v = {1, 2, 3, 4, 5};
auto iter = find(v.begin(), v.end(), 3);

// Found: 3 at position 2
if (iter != v.end())
    cout << "Found: " << *iter << " at position " << (iter - v.begin()) << '\n';
else
    cout << "Not found\n";
1
2
3
4
5
6
// 문자열에서 문자 찾는 예시
string str = "Hello";
auto iter = find(str.begin(), str.end(), 'e');

if (iter != str.end())
    cout << iter - str.begin() << '\n'; // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 구조체 벡터에서 찾는 예시
struct Person
{
    string name;
    int age;
    
    bool operator==(const Person& other) const
    {
        return name == other.name && age == other.age;
    }
};

vector<Person> people = {
    {"Alice", 20},
    {"Bob", 25},
    {"Charlie", 30}
};

auto iter = find(people.begin(), people.end(), Person{"Bob", 25});
if (iter != people.end())
    cout << iter->name << " " << iter->age << '\n'; // Bob 25

find 함수는 두 값이 같은지 판단할 때, operator==를 사용한다.

따라서 사용자 정의 타입의 객체를 비교하려면 해당 객체에 == 연산자를 오버로딩해야 한다.



binary_search는 컨테이너에서 값을 선형 검색이 아닌 이진 탐색($O(logN)$)으로 찾는 함수이다.

이분 탐색의 특성 상 컨테이너가 정렬되어 있어야 한다.

first ~ last 범위 내에서 value와 일치하는 원소가 있다면 true를 반환하고, 아니라면 false를 반환한다.

1
2
3
4
5
6
7
// 함수 원형
template <class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val);

// 비교함수 사용 버전
template <class ForwardIterator, class T, class Compare>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
1
2
3
4
// 벡터에서 정수 찾는 예시
vector v = {1, 3, 4, 5, 7, 9, 11};
bool exists = binary_search(v.begin(), v.end(), 5);
cout << boolalpha << exists; // true
1
2
3
4
// 문자열에서 문자 찾는 예시
string str = "Hello";
auto exists = binary_search(str.begin(), str.end(), 'e');
cout << boolalpha << exists << '\n'; // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 구조체 벡터에서 찾는 예시
struct Person
{
    string name;
    int age;
};
vector<Person> v =
{
    {"Alice", 20},
    {"Bob", 25},
    {"Charlie", 30},
    {"David", 35}
};

Person target = {"", 30};
auto comp = [](const Person& a, const Person& b) {
    return a.age < b.age;
};
sort(v.begin(), v.end(), comp);
auto exists = binary_search(v.begin(), v.end(), target, comp);
cout << boolalpha << exists << '\n'; // true


lower_bound


lower_bound 함수는 찾으려는 값 이상이 처음 나타나는 위치를 이분 탐색을 통해 찾는다.

찾으려는 값 이상이 존재한다면 처음 나타나는 위치의 반복자를 반환하고, 그렇지 않다면 last를 반환한다.

1
2
3
4
5
6
7
// 함수 원형
template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& val);

// 비교함수 사용 버전
template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
1
2
3
4
5
// 정수 벡터에서 찾는 예시
vector v = {1, 2, 2, 2, 3, 4, 5, 10};
auto iter = lower_bound(v.begin(), v.end(), 8);
cout << distance(v.begin(), iter) << '\n';  // 7
cout << *iter << '\n';  // 10

마찬가지로 사용자 정의 자료형에서 사용하려면 binary_search에서 본 것처럼 비교 함수를 정의해야 한다.


upper_bound


upper_bound 함수는 찾으려는 값보다 큰 값이 처음 나타나는 위치이분 탐색을 통해 찾는다.

찾으려는 값보다 큰 값이 존재한다면 처음 나타나는 위치의 반복자를 반환하고, 그렇지 않다면 last를 반환한다.

1
2
3
4
5
6
7
// 함수 원형
template <class ForwardIterator, class T>
ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, const T& val);

// 비교함수 사용 버전
template <class ForwardIterator, class T, class Compare>
ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
1
2
3
4
5
// 정수 벡터에서 찾는 예시
vector v = {1, 2, 2, 2, 3, 4, 5};
auto iter = upper_bound(v.begin(), v.end(), 2);
cout << distance(v.begin(), iter) << '\n'; // 4
cout << *iter << '\n'; // 3


equal_range


equal_rangelower_bound와 upper_bound의 쌍을 반환한다.

이는 찾고자 하는 원소의 범위를 알 수 있어 유용하다.

1
2
3
4
5
6
7
8
9
// 함수 원형
template <class ForwardIterator, class T>
pair<ForwardIterator,ForwardIterator> 
equal_range(ForwardIterator first, ForwardIterator last, const T& val);

// 비교함수 사용 버전
template <class ForwardIterator, class T, class Compare>
pair<ForwardIterator,ForwardIterator> 
equal_range(ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
1
2
3
4
5
6
vector v = {1, 2, 2, 2, 3, 4, 5};
auto range = equal_range(v.begin(), v.end(), 2);
auto start = distance(v.begin(), range.first);
auto end = distance(v.begin(), range.second);

cout << start << "~" << end << '\n';  // 1~4


distance


distance 함수는 두 반복자 사이의 요소 개수를 반환한다.

begin과의 거리를 구하면 해당 반복자의 인덱스를 구할 수 있기에 편리하다.

1
2
3
4
// 함수 원형
template <class InputIterator>
typename iterator_traits<InputIterator>::difference_type
    distance(InputIterator first, InputIterator last);
1
2
3
4
5
vector v = {1, 2, 3, 4, 5};
auto it = find(v.begin(), v.end(), 3);
auto index = distance(v.begin(), it); // 2

cout << index << '\n';


참고

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.