매번 lower bound, upper bound 생각하는게 헷갈려 정리해본다.
상황
다음과 같이 내림차순이 아닌(오름차순이거나 같은) 배열이 있다고 가정해보자.
[1,2,2,2,2,3,4,5,6,7,8]
이때 2 값을 갖는 가장 작은 인덱스와 가장 큰 인덱스를 어떻게 구할것인가?
여기서 lower bound, upper bound 개념이 나온다.
- lower bound : key 값 이상인 값들중 가장 작은 인덱스
- upper bound : key 값 초과인 값들중 가장 작은 인덱스
// [1,2,2,2,2,3,4,5,6,7,8]
// lower bound
System.out.println(lowerBound(2)); // 1
System.out.println(lowerBound(1.5)); // 1
System.out.println(lowerBound(0)); // 0
System.out.println(lowerBound(8)); // 10
// [1,2,2,2,2,3,4,5,6,7,8]
// upper bound
System.out.println(upperBound(2)); // 5
System.out.println(upperBound(1.5)); // 1
System.out.println(upperBound(0)); // 0
System.out.println(upperBound(8)); // 11
이분 탐색
이분 탐색(binary search)를 적절하게 이용하면 찾을 수 있을거같다.
기본 적인 이분 탐색 알고리즘은 다음과 같다.
int binarySearch(int s,int e,int[] array,int key) {
while(s<=e) {
int mid=(s+e)/2;
int midValue=array[mid];
// midValue 가 key 와 같으면 mid 를 리턴한다.
if(midValue==key) return mid;
// midValue 가 key 보다 크면 왼쪽에서 찾는다.
else if(midValue>key) e=mid-1;
// midValue 가 key 보다 작으면 오른쪽에서 찾는다.
else s=mid+1;
}
// 찾지 못했으면 -1 을 리턴한다.
return -1;
}
구현 방법은 s,e를 설정해두고 중간값(midValue)을 구해 key 와 비교한다.
midValue 가 key 보다 크면 왼쪽에서 찾고 작으면 오른쪽에서 찾는다.
예를 들어 다음 배열에서 값 7 인덱스를 찾는다고 하면
[5,6,7,8,9,10,11,12]
1) s=0, e=7, mid=3, midValue=8, key=7 key(7) < midValue(8) 이므로 왼쪽에서 찾는다.
[5,6,7,8]
2) s=0, e=3, mid=1, midValue=6, key=7 key(7) > midValue(6) 이므로 오른쪽에서 찾는다.
[7,8]
3) s=2, e=3, mid=2, midValue=7, key=7 key(7) == midValue(7) 이므로 mid(2)를 리턴한다.
[7]
이제 이분 탐색을 활용해 lower bound 와 upper bound 를 구해보자.
lower bound
lower bound : key 값 이상인 값들중 가장 작은 인덱스
lower bound 의 예제 값들은 다음과 같았다.
// [1,2,2,2,2,3,4,5,6,7,8]
// lower bound
System.out.println(lowerBound(2)); // 1
System.out.println(lowerBound(1.5)); // 1
System.out.println(lowerBound(0)); // 0
System.out.println(lowerBound(8)); // 10
예제를 보면 중앙값이 key 보다 같거나 크면 왼쪽에서 찾아야한다.
다만 같은 경우에도 왼쪽에서 찾으므로 경계값(mid)도 e에 포함시킨다.
구현하면 다음과 같다.
int lowerBound(int s,int e,int[] array,int key) {
while(s<e) {
int mid=(s+e)/2;
int midValue=array[mid];
// midValue 가 key 보다 같거나 크면 왼쪽에서 찾는다.
if(midValue>=key) e=mid;
// midValue 가 key 보다 작으면 오른쪽에서 찾는다.
else s=mid+1;
}
// s==e 이므로 s 나 e 를 리턴해도 된다.
return e;
}
upper bound
upper bound : key 값 초과인 값들중 가장 작은 인덱스
upper bound 의 예제 값들은 다음과 같았다.
// [1,2,2,2,2,3,4,5,6,7,8]
// upper bound
System.out.println(upperBound(2)); // 5
System.out.println(upperBound(1.5)); // 1
System.out.println(upperBound(0)); // 0
System.out.println(upperBound(8)); // 11
lower bound 와 비슷하지만 key 값이 최대갑보다 크다면 list 밖의 인덱스가 리턴된다.
이번에는 중앙값이 key 보다 작거나 같으면 오른쪽에서 찾아야한다.다만 같은 경우 s=mid+1 로 경계값(mid)를 포함시키지 않는다.
int upperBound(int s,int e,int[] array,int key) {
while(s<e) {
int mid=(s+e)/2;
int midValue=array[mid];
// midValue 가 key 보다 작으면 오른쪽에서 찾는다.
if(midValue<=key) s=mid+1;
// midValue 가 key 보다 크면 왼쪽에서 찾는다.
else e=mid;
}
// s==e 이므로 s 나 e 를 리턴해도 된다.
return e;
}
'Algorithm' 카테고리의 다른 글
[입출력] Java FastIO For Competitive programming (0) | 2024.04.19 |
---|