PS/Algorithm

이진탐색 Binary Search, Lower/Upper Bound, Parametric Search

hyelie 2022. 6. 22. 12:32

이진 탐색 : O(logn)

개념

 Binary search는 정렬된 array에서 어떤 값 k를 찾는 방법이다. 이 또한 divide and conquer를 사용하는 방식으로, 재귀방정식을 세우면 아래와 같다.


$T\left(n\right)\ =\ T\left(\frac{n}{2}\right)\ +\ c$


 따라서 시간복잡도는 O(logn).

 다른 방식으로,

처음 n개를 탐색할 때, 중앙값을 보고 탐색할 부분을 절반으로 나눈다.
다음 n/2개를 탐색할 때, 중앙값을 보고 탐색할 부분을 절반으로 나눈다.
...
마지막 것이 탐색된다.

 즉, n개의 input이 있을 때 탐색은 logn번 진행한다.


반복문으로 탐색 : O(logn)

int search(vector<int>& v, int input){
    int start = 0, end = v.size()-1, mid;
    while(start <= end){
        mid = (start + end) / 2;
        if(v[mid] < input){
            start = mid + 1;
        } else if(v[mid] == input){
            return mid; // found
        } else{
            end = mid - 1;
        }
    }
    return -1; // not found
}

 코드의 핵심은
 - v는 정렬되어 있어야 한다
 - start <= end까지 반복
 - mid가 더 작은 경우 -> end를 mid-1로 당김
 - mid가 더 큰 경우 -> start를 mid+1로 당김

 

 

STL 이용 : O(logn)

 단순 해당 값이 있는지 없는지만 찾으려면 STL을 사용하는 것이 더 편리할 것이다.

template <class ForwardIterator, class T>
  bool binary_search (ForwardIterator first, ForwardIterator last, const T& val)


binary_search(v.begin(), v.end(), value);

 

 

 

Lower Bound, Upper Bound : O(logn)

 lower bound는 'array에서 탐색하는 값 k보다 크거나 같은 값이 처음 나오는 위치'이고, upper bound는 'array에서 탐색하는 값 k보다 큰 값이 처음 나오는 위치'이다. 이 용어들은 자주 사용하니 기억해 두자.

 

 

반복문으로 탐색

int lower_bound(vector<int> &v, int target)
{
	int mid, start = 0, end = v.size();
	while (start < end) 
	{
		mid = (start + end) / 2; 
		if (v[mid] >= target) // 중간값이 target보다 크거나 같은 경우 해당 index까지 end를 당김.
			end = mid; 
		else start = mid + 1; // 중간값이 target보다 작으면 mid+1까지 start를 당김.
	}
	return end;
}

int upper_bound(vector<int> &v, int target)
{
	int mid, start = 0, end = v.size();
	while (start < end) 
	{
		mid = (start + end) / 2; 
		if (v[mid] > target) // 중간값이 target보다 큰 경우 해당 index까지 end를 당김.
			end = mid;
		else start = mid + 1; // 중간값이 target보다 작으면 mid+1까지 start를 당김.
	}
	return end;
}

 코드의 핵심은
 - start < end까지 반복
 - 더 작은 값을 탐색해야 하면 end = mid
 - 더 큰 값을 탐색해야 하면 start = mid + 1
 - lower bound의 경우 크거나 같은 값을 찾으므로 조건문은 >=, upper bound의 경우 큰 값을 찾으므로 조건문은 >. 이를 좀 더 쉽게 이해하는 방법은, [if문 안에 있는 조건을 민족하는 제일 작은 index를 찾는다]고 생각하면 된다. Lower bound의 경우 target보다 크거나 같은 값 중 제일 작은 index를, upper bound의 경우는 target보다 큰 값 중 제일 작은 index를 찾는 것이다.
 - 만약 arr에 없는 값을 탐색 할 시엔은 v.size()값을 가지는 end가 리턴됨.

 

 

 STL 이용 : O(logn)

lower_bound(v.begin(), v.end(), value); // 결과로 iterator가 리턴됨
lower_bound(v.begin(), v.end(), value) - v.begin(); // index

upper_bound(v.begin(), v.end(), value); // 결과로 iterator가 리턴됨
upper_bound(v.begin(), v.end(), value) - v.begin(); // index

만약 해당하는 값이 없으면 .end()를 리턴한다.

 

 

 

Parametric Search

 optimization problem을 decision problem으로 바꿔 푸는 것. '전체 중에서 최적해를 찾는 문제(optimization problem)'를 '특정 값은 답(해)이 될 수 있는가?(decision problem)'로 바꿔 풀겠다는 것이다. 그리고 이분 탐색과 마찬가지로 그 '특정 값'이 해가 되냐, 그렇지 않냐에 따라 다음으로 찾을 범위를 바꾼다. 이분 탐색과 유사하게 풀 수 있다는 것이다.

 

 (보편적으로) parametric search로 풀어야 하는 문제들은 최적화 문제로써 풀기는 어렵지만

  • 정답이 속한 집합의 크기가 long long, 또는 int과 같이 크지
  • 결정 문제로 바꾸었을 때(특정 값은 답이 될 수 있는가?) 쉽게 풀리며
  • 가능한 답이 연속적(정수) - 부연 설명을 하자면 예를 들어, 최댓값을 구하는 문제의 경우 어떤 값 k가 조건을 만족한다면 k보다 작은 모든 수들은 조건을 만족해야 한다는 것 -

위 특징들이 있으며, 이러한 문제가 있다면 parametric search로 접근해 보자.