O(n)이 걸리는 선형 탐색을 O(log n)로 줄일 수 있는 방법이다.
일반적으로 이분탐색이라 하면
// 주어진 조건 내에서 최솟값을 구하라고 할 때
int left = 0, right = MAX;
while(left <= right){
int mid = (left + right)/2;
if(mid < 주어진 조건){
right = mid+1;
}else {
left = mid;
}
}
return left;
// 주어진 조건 내에서 최댓값을 구하라고 할 때
int left = 0, right = MAX;
while(left <= right){
int mid = (left + right)/2;
if(mid > 주어진 조건){
left = mid-1;
}else {
right = mid;
}
}
return right;
요런 식으로 식을 세운다.
관련문제는:
[백준] 나무자르기
[백준] 랜선 자르기
[백준] 공유기 설치
가 있다.
이분탐색의 확장:
c++의 lower_bound와 upper_bound가 있다.
www.cplusplus.com/reference/algorithm/lower_bound/
lower_bound는 찾는 값보다 크거나 같은 값이 처음으로 등장하는 iterator를 리턴
upper_bound는 찾는 값보다 큰 값이 처음으로 등장하는 iterator를 리턴한다.
주의할 점은 두 함수 모두 벡터/배열을 정렬 후에 사용해야 한다는 점이다.
두 함수 모두 iterator를 리턴하므로 사용 시에 벡터의 경우 v.begin()을, 배열의 경우 주소값인 arr를 빼면 위치가 나온다.
#include <algorithm>
#include <iostream>
using namespace std;
int main(void){
vector<int>::iterator low,up;
sort(v.begin(), v.end()); // 정렬 필수
low = lower_bound(v.begin(), v.end(), val);
// 찾고자 하는 값 val보다 크거나 같은 값이 처음으로 등장하는 iterator를 리턴
up = upper_bound(v.begin(), v.end(), val);
// 찾고자 하는 값 val보다 큰 값이 처음으로 등장하는 iterator를 리턴
cout<<lower_bound(v.begin(), v.end(), val) - v.begin() + 1 << "\n";
}
lower_bound와 upper_bound를 이분탐색을 사용해서 직접 구현할 수 있다.
int lower_bound(int arr[], int len, int n){
int left = 0, right = arr[len-1];
while(left<right){
int mid = (left + right)/2;
if(arr[mid] >= n) right = mid;
else left = mid+1;
}
return right;
}
int upper_bound(int arr[], int len, int n){
int left = 0, right = arr[len-1];
while(left<right){
int mid = (left + right)/2;
if(arr[mid] > n) right = mid;
else left = mid+1;
}
return right;
}
그렇다면 이게 언제 쓰이느냐..
범위 안에 해당하는 숫자의 개수를 찾는데 유용하다.
www.usaco.org/index.php?page=viewproblem2&cpid=666
또는 LIS 계산할때..정말 유용하다.
다음에 자세히 다뤄볼 예정
암튼 자주 등장하는 주제이니 꼭 기억하자
'CS > 알고리즘' 카테고리의 다른 글
[알고리즘] Directed Graph에서 사이클 찾기 (0) | 2023.04.15 |
---|---|
[알고리즘] 최단 경로 역추적 (0) | 2021.04.11 |
[알고리즘] 선형 자료 구조에서 탐색(Search) (0) | 2021.01.21 |
[알고리즘] Dijkstra 다익스트라 알고리즘 (0) | 2020.12.30 |
[알고리즘] 정렬 - quick sort (0) | 2020.11.29 |
댓글