Lower Bound는 이분 탐색에 기반한 알고리즘으로 오름차순 정렬되어있는 배열과 기준 값 이 주어졌을 때 기준 값 이상이 처음 나타나는 위치(Index)를 찾는 알고리즘입니다.
Lower Bound 알고리즘의 특별한 점은 무엇일까요?
기준 값 값 이상이 처음 나타나는 위치(Index)를 찾기 위해 단순히 배열의 첫 번째 위치부터 순차적으로 탐색한다면 최악의 경우 배열의 길이만큼 끝까지 탐색을 해야 할 수 있고, 이 경우 시간복잡도는 이 될 것입니다.
하지만, 배열의 처음부터 탐색하지 않고 이분 탐색을 활용한다면 의 시간복잡도로 탐색이 가능하며, 이것이 Lower Bound 알고리즘의 특별한 점이라고 할 수 있습니다.
이분 탐색은 전체 배열의 중간 값과 기준 값을 비교하며 원하는 값을 찾아가는 방식입니다. Lower Bound에서도 이분 탐색과 동일하게 left, mid, right 값을 갱신하며 기준 값 이상이 처음 나타나는 위치(Index)를 찾아갑니다.
이분 탐색 기반의 Lower Bound 알고리즘을 함께 단계별로 살펴봅시다.
특히, 주어진 배열에 기준 값 의 존재 유무에 따라 Case를 나누어 확인해보겠습니다.
arr = [1, 2, 3, 3, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 18] (배열의 길이 = 15)배열의 길이가 15이고, 초기에는 기준 값의 탐색 영역이 배열 전체이므로 아래와 같이 설정할 수 있습니다.
mid = (left + right) / 2 = 7로 계산합니다.arr[mid] = 8이며, 기준 값 5는 8보다 왼쪽 영역에 위치하므로 탐색 영역을 더 좁혀볼 수 있습니다.mid = (left + right) / 2 = 3arr[mid] = 3이며, 기준 값 5는 3보다 오른쪽 영역에 위치하므로 탐색 영역을 더 좁혀볼 수 있습니다.mid = (left + right) / 2 = 5arr[mid] = 6이며, 기준 값 5는 6보다 왼쪽 영역에 위치하므로 탐색 영역을 더 좁혀볼 수 있습니다.arr = [1, 2, 3, 3, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 18] (배열의 길이 = 15)배열의 길이가 15이고, 기준 값 12를 찾기 위한 탐색 영역은 배열 전체이므로, 아래와 같이 설정할 수 있습니다.
mid = (left + right) // 2 = 7로 계산합니다.arr[mid] = 8이며, 기준 값 12는 8보다 크기 때문에 탐색 영역을 오른쪽으로 좁힙니다.mid = (left + right) // 2 = 11arr[mid] = 13이며, 기준 값 12는 13보다 작기 때문에 탐색 영역을 왼쪽으로 좁힙니다.mid = (left + right) // 2 = 9arr[mid] = 10이며, 기준 값 12는 10보다 크기 때문에 탐색 영역을 다시 오른쪽으로 좁힙니다.mid = (left + right) // 2 = 10arr[mid] = 11이며, 기준 값 12는 11보다 크기 때문에 탐색 영역을 다시 오른쪽으로 좁힙니다.def lower_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
int lower_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;
}