2021년 상반기 카카오 인턴 코딩 테스트에 응시했었는데, 당시 3번 문제에 upper_bound, lower_bound를 사용했었다.
그 당시에 조금 헤맸던 기억이 있어서 이번 기회에 한번 정리해보려 한다.
우선 c++ reference(https://www.cplusplus.com/reference/algorithm/upper_bound/)를 찾아보면
Return iterator to upper bound
Returns an iterator pointing to the first element in the range [first,last) which compares greater than val.
The elements are compared using operator< for the first version, and comp for the second. The elements in the range shall already be sorted according to this same criterion (operator <or comp), or at least partitioned with respect to val.
The function optimizes the number of comparisons performed by comparing non-consecutive elements of the sorted range, which is specially efficient for random-access iterators.
Unlike lower_bound, the value pointed by the iterator returned by this function cannot be equivalent to val, only greater.
다음과 같이 저술되어 있다.
첫 문장을 보면 내가 입력한 value보다 큰 첫 번째 원소를 가리키는 iterator를 반환한다 라고 되어있다.
즉 1 1 2 2 2 3 3과 같은 배열에서 2를 입력값으로 주면 3의 첫 위치(1 1 2 2 2 3 3)를 가리키는 iterator를 반환한다고 되어있다.
두 번째 문단을 살펴보면, upper bound에는 두 가지 버전이 있는데 두 버전의 원형은 다음과 같다.
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);
이를 보면 첫 번째 버전은 별도의 비교 연산을 정의하지 않은 상태로, 시작 위치, 종료 위치, 값 이렇게 세 개만 인자로 전달하는 것을 볼 수 있고, 두 번째 버전은 비교 연산을 인자로 같이 전달하여 함수를 호출하는 것을 볼 수 있다.
첫 번째 버전은 별도의 비교 연산을 정의하지 않았기 때문에 < 즉 우리가 기본적으로 알고 있는 오름차순으로 정렬되어 있다고 가정한다.
그리고 val 바로 다음 원소의 시작 iterator를 반환하기 때문에 절대로 val과 같은 값을 가리키는 iterator가 반환될 수 없다고 되어있다.
비슷한 맥락으로 lower bound에 대한 reference도 참고해 보길 바란다. (https://www.cplusplus.com/reference/algorithm/lower_bound/)
간략히 요약하자면, lower bound의 경우
Returns an iterator pointing to the first element in the range [first, last) which does not compare less than val.
즉 val보다 작지 않은 첫 번째 원소의 시작점을 반환한다고 되어있다.
위의 내용을 바탕으로 간단한 예시 코드를 한번 작성해 보았다.
#include <bits/stdc++.h>
using namespace std;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
vector<int> vec;
vec.push_back(1);
vec.push_back(1);
vec.push_back(2);
vec.push_back(2);
vec.push_back(2);
vec.push_back(3);
vec.push_back(3);
// 1 1 2 2 2 3 3
int ub = *upper_bound(vec.begin(), vec.end(), 2); // 3
int lb = *lower_bound(vec.begin(), vec.end(), 2); // 2
int ub_range = (int) (upper_bound(vec.begin(), vec.end(), 2) - vec.begin());
int lb_range = (int) (lower_bound(vec.begin(), vec.end(), 2) - vec.begin());
cout << ub << " " << lb << endl;
cout << ub_range << " " << lb_range << endl;
return 0;
}
vector에 int형으로 1, 1, 2, 2, 2, 3, 3을 입력하였고, 별도의 compare함수는 정의하지 않았다.
위의 내용을 바탕으로 도식화해보면 upper_bound, lower_bound가 반환하는 iterator가 가리키는 위치는
다음과 같다.
즉 val값으로 2를 주었을 때 lower_bound의 경우 2보다 작지 않은 값 (=2)의 시작점의 위치를 가리키게 되고,
upper_bound의 경우 2보다 큰 값 (=3)의 시작점의 위치를 가리키게 된다.
따라서 위의 코드를 실행하였을 때 나타나는 결괏값은
3, 2 -> 각각 iterator가 가리키는 주소의 값
5, 2 -> 각각 iterator가 가리키는 주소가 시작점으로부터 몇 번째 인덱스인지 확인
으로 예측할 수 있다.
실제로 확인해보면,
예측한 결과대로 나오는 것을 확인할 수 있다.
그러면 만약에 배열에 없는 값을 val로 주면 어떻게 될까?
int ub_range = (int) (upper_bound(vec.begin(), vec.end(), 0) - vec.begin());
int lb_range = (int) (lower_bound(vec.begin(), vec.end(), 4) - vec.begin());
주어진 코드를 다음과 같이 수정하여 실행해 보았다.
lower bound의 경우 val보다 작지 않은, 즉 이상이 되는 첫 번째 값을 가리키는 iterator를 반환하기 때문에 아마 0번째 인덱스가 반환될 것이라 예측되고, upper bound의 경우 val보다 큰 첫번째 원소를 가리키는 iterator를 반환하기 때문에 배열의 마지막 원소인 6번 인덱스의 다음인 7번이 반환될 것이라 예측할 수 있다.
다음과 같이 출력되는 것을 확인할 수 있다.
*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.
'Algorithm > Algorithm Logic' 카테고리의 다른 글
[C++/Algorithm] 정렬 알고리즘 이해하고 구현하기 (Merge, Quick) #2 (0) | 2021.06.27 |
---|---|
[C++/Algorithm] 정렬 알고리즘 이해하고 구현하기 (bubble, insertion, selection) #1 (0) | 2021.06.27 |
[C++/Algorithm] 순열과 조합 구현하기 (0) | 2021.06.23 |
[C++/Algorithm] priority_queue 사용법 (0) | 2021.06.21 |
[C++/Algorithm] <bits/stdc++.h> 사용하기 (1) | 2021.06.21 |