정렬된 배열에서 가운데 값과 찾는 값을 비교해 매번 절반을 버리며 찾는 방법. 원소가 100만 개여도 20번 남짓이면 끝난다(O(log n)). 처음부터 하나씩 확인하는 선형 탐색(Linear Search)은 O(n)이다.
function binarySearch(arr: number[], target: number) {
let lo = 0
let hi = arr.length - 1
while (lo <= hi) {
const mid = Math.floor((lo + hi) / 2)
if (arr[mid] === target) return mid
if (arr[mid] < target) lo = mid + 1
else hi = mid - 1
}
return -1
}- 정렬이 전제다. 한 번만 찾는다면 정렬 비용(
O(n log n))이 선형 탐색보다 크다. 여러 번 찾을 때 이득이다 - 경계 실수(
<와<=,mid ± 1)가 가장 흔한 버그다. 원소 0개·1개·2개로 꼭 확인한다 - "조건을 처음 만족하는 위치 찾기"로 일반화하면 정답 값의 범위를 탐색하는 문제(파라메트릭 서치, Parametric Search)에도 쓴다
- 같은 원리가 이진 탐색 트리와 DB 인덱스의 B-트리(B-Tree)에도 있다(DB 인덱스와 트레이드오프)
git bisect는 커밋 이력에 대한 이진 탐색이다(git bisect로 원인 커밋 찾기)
이진 탐색은 분할 정복의 가장 단순한 예다. 비교 삼아, 긴 문자열 안에서 짧은 문자열을 찾는 단순 문자열 탐색(Naive String Search)은 위치마다 한 글자씩 대조하는 O(n·m) 방법이다. 복잡도 감각은 빅오 표기법 참고.