Upper Bound는 이분 탐색에 기반한 알고리즘으로 오름차순 정렬된 배열과 기준 값 이 주어졌을 때 기준 값 보다 큰 값이 처음 나타나는 위치(Index) 를 찾는 알고리즘입니다.
배열을 처음부터 끝까지 순차적으로 탐색한다면 최악의 경우 배열의 길이만큼 끝까지 탐색해야 할 수 있습니다. 이 경우 시간 복잡도는 이 될 것입니다.
그러나 배열의 처음부터 순차 탐색하지 않고 이분 탐색을 사용한다면, 의 시간 복잡도로 더 효율적으로 탐색할 수 있습니다.
이 부분이 Upper Bound 알고리즘의 특별한 점입니다.
Upper Bound는 이분 탐색을 기반으로 left, mid, right 값을 갱신하며 기준 값보다 큰 값이 처음 나타나는 위치를 찾는 방식입니다. 배열에 기준 값 이 존재하는지에 따라 Case를 나누어 살펴보겠습니다.
arr = [1, 2, 3, 3, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 18] (배열의 길이 = 15)배열의 길이가 15이고, 초기 탐색 영역은 배열 전체이므로 아래와 같이 설정할 수 있습니다.
mid = (left + right) // 2 = 7arr[mid] = 8이며, 기준 값 5보다 크므로 더 작은 영역으로 탐색 영역을 좁힙니다.mid = (left + right) // 2 = 3arr[mid] = 3으로 기준 값 5보다 작기 때문에 탐색 영역을 더 오른쪽으로 좁힙니다.mid = (left + right) // 2 = 5arr[mid] = 6으로, 기준 값보다 크기 때문에 탐색 영역을 왼쪽으로 좁힙니다.arr = [1, 2, 3, 3, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 18] (배열의 길이 = 15)배열의 길이가 15이고, 기준 값 12를 찾기 위한 탐색 영역은 배열 전체이므로 아래와 같이 설정할 수 있습니다.
mid = (left + right) // 2 = 7arr[mid] = 8로, 기준 값보다 작기 때문에 탐색 영역을 오른쪽으로 좁힙니다.mid = (left + right) // 2 = 11arr[mid] = 13으로, 기준 값보다 크기 때문에 탐색 영역을 왼쪽으로 좁힙니다.mid = (left + right) // 2 = 9arr[mid] = 10으로, 기준 값보다 작기 때문에 탐색 영역을 오른쪽으로 좁힙니다.mid = (left + right) // 2 = 10arr[mid] = 11로, 기준 값보다 작기 때문에 탐색 영역을 다시 오른쪽으로 좁힙니다.def upper_bound(nums, target):
left, right = 0, len(nums)
while left < right: # left와 right가 만나는 지점이 target보다 큰 값이 처음 나오는 위치
mid = (left + right) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid
return left
Python에서는 bisect 모듈에 upperbound 메소드가 존재합니다.
int upper_bound(const std::vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // vector의 크기
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid;
}
}
return right;
}