[Algorithm] 이진탐색 알고리즘

GIGI·2024년 8월 27일

알고리즘

목록 보기
1/6
post-thumbnail

순차 탐색 vs. 이진 탐색

  • 문제: 아래의 리스트에서 값이 12인 원소의 위치를 찾고자 한다. 어떻게 찾을 수 있을까?

순차 탐색: 리스트 안에 있는 특정한 데이터를 찾기 위해 앞에서부터 하나씩 확인한다.

[ 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 ]
  • 탐색을 위한 시간 복잡도: O(N)

이진 탐색: 이진 탐색을 수행할 때는 시작점(left)과 끝점(end)을 기준으로 탐색 범위를 명시한다.

[1단계]
[ 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 ]
  Left               Mid                Right

[2단계]
[ 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 ]
                     Left   Mid        Right

[3단계]
[ 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 ]
                          Left Mid     Right

[4단계]
[ 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 ]
                          Left=Right=Mid
  • 탐색을 위한 시간 복잡도: O(logN)

이진 탐색 문제 유형의 대표적인 사례

  • 매우 넓은(억 단위 이상) 탐색 범위에서 최적의 해를 찾아야 하는 경우
  • 데이터를 정렬한 뒤에 다수의 쿼리(query)를 날려야 하는 경우

이진 탐색 코드 예시 (재귀 함수)

function binarySearch(arr, target, start, end) {
    if (start > end) return -1; // 찾지 못한 경우
    
    let mid = parseInt((start + end) / 2); // 중간 값 인덱스 계산
    
    // 값을 찾은 경우
    if (arr[mid] === target) return mid;
    
    // 중간점의 값보다 찾고자 하는 값이 작은 경우 왼쪽 확인
    else if (arr[mid] > target) return binarySearch(arr, target, start, mid - 1);
    
    // 중간점의 값보다 찾고자 하는 값이 큰 경우 오른쪽 확인
    else return binarySearch(arr, target, mid + 1, end);
}

// n(원소의 개수)와 target(찾고자 하는 값)
let arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
let result = binarySearch(arr, target, 0, n - 1);

if (result == -1) console.log('원소가 존재하지 않습니다.');
else console.log(`${result + 1}번째 원소입니다.`);

이진 탐색 코드 예시 (반복문)

function binarySearch(arr, target, start, end) {
	while (start<=end) {
    	let mid = parseInt((start+end)/2);
      //찾은 경우 중간점 인덱스 반환
      if (arr[mid] == target) end = mid-1;
      // 중간점의 값보다 찾고자 하는 값이 작은 경우 왼쪽 확인
      else if (arr[mid > target) end = mid-1;
      // 중간점의 값보다 찾고자 하는 값이 큰 경우 오른쪽 확인 
      else start = mid + 1;
    }
  return -1;
}

let n = 10;
let target = 7;
arr = [1,3,5,7,9,11,13,15,17,19];

let result = binarySearch(arr, target, 0, n-1);
if(result == -1) console.log('원소가 존재하지 않습니다');
else console.log(`${result+1}번째 원소입니다`);

정렬된 배열에서 특정 원소의 개수 구하기

[값이 특정 범위에 속하는 원소의 개수 구하기]

  • 정렬된 배열에서 값이 특정 범위에 해당하는 원소의 개수를 계산하는 것을 요구하는 경우가 종종 있다.
  • 이를 해결하기 위해 lowerBound() 함수와 upperBound() 함수를 사용할 수 있다.

하한선과 상한선 함수

  • lowerBound(arr, x) : 정렬된 순서를 유지하면서 배열 arr에 x를 넣을 가장 왼쪽 인덱스를 반환
  • upperbound(arr, x) : 정렬된 순서를 유지하면서 배열 arr에 x를 넣을 가장 오른쪽 인덱스를 반환
function lowerBound(arr, target, start, end) {
	while(start < end) {
    	let mid = parseInt((start+end) / 2);
      	if(arr[mid] >= target) end = mid; // 최대한 왼쪽으로 이동
      	else start = mid+1;
    }
  return end;
}

function upperBound(arr, target, start, end) {
	while(start < end) {
    	let mid = parseInt((start+end) / 2);
      	if(arr[mid] > target) end = mid; 
      	else start = mid+1;// 최대한 오른쪽으로 이동
    }
  return end;
}
  • countByRange() : 정렬된 배열에서 값이 특정 범위에 속하는 원소의 개수를 계산한다.
  • upperBound(), lowerBound() 함수를 통해 구현 가능
// 값이 [leftValue, rightValue]인 데이터의개수를 반환하는 함수 
function countbyRange(arr, leftValue, rightValue){
	let rightIndex = upperBound(arr, rightValue, 0, arr.length);
  	let leftIndex = lowerBound(arr, leftValue, 0, arr.length);
  	return rightIndex - leftIndex;
}

파라메트릭 서치 이해하기

  • 이진 탐색 조건 : 변경할(최적화할) 값 x에 대하여 f(x)가 단조 증가 혹은 단조 감소
    - 단조 증가 함수: x ≤ y이면 f(x) ≤ f(y)인 경우
    - 일반적으로 조건(constraint)은 f(x)에 대하여 정의된다.

파라메트릭 서치(Parametric Search)란?

  • 최적화 문제를 결정 문제('예' 혹은 '아니오')로 바꾸어 해결하는 기법이다.
  • 예시: 특정한 조건을 만족하는 가장 알맞은 값을 빠르게 찾는 최적화 문제
    일반적으로 코딩 테스트에서 파라메트릭 서치 문제는 이진 탐색을 이용하여 해결할 수 있다.

max x
s. t. f(x) > C
where f(x) is monotone decreasing function.

profile
이제 누구도 날 막을 수 없다!!!!!!!!!!

0개의 댓글