데이터 더미에서 값을 찾는 탐색(search)은 코테의 기본기예요. 하나씩 훑는 선형 탐색과, 정렬된 배열을 절반씩 줄이는 이진 탐색의 차이를 확실히 잡아봐요.
42라는 값을 찾거나, 그 값의 인덱스를 알아내는 상황이죠.
💡 도서관에서 책 한 권을 찾는다고 생각해봐요. 책장을 처음부터 하나씩 보는 방법(선형)과, 정렬된 목록에서 중간을 펼쳐 좁혀가는 방법(이진)이 있어요.
O(n)은 "n에 비례해서 늘어난다", O(log n)은 "n이 아무리 커져도 아주 천천히 늘어난다"는 뜻이에요. 정확한 초 단위 속도가 아니라 "몇 배로 느려지는가"라는 경향을 보는 지표라고 생각하면 쉬워요.선형(순차) 탐색은 배열의 맨 앞부터 끝까지 하나씩 값을 비교하며 찾는 값을 만날 때까지 훑는 방법이에요. 정렬이 돼 있지 않아도 그대로 쓸 수 있는 게 장점이에요.
function linearSearch(arr, target) {
for (let i = 0; i < arr.length; i++) { // 앞에서부터 하나씩
if (arr[i] === target) {
return i; // 찾으면 그 위치(인덱스) 반환
}
}
return -1; // 끝까지 없으면 -1
}
linearSearch([7, 2, 9, 4], 9); // → 2
찾는 값이 맨 끝에 있거나 아예 없으면 배열 전체를 다 훑어야 해요. 그래서 최악의 경우 비교 횟수는 n번 → O(n).
💡 정렬 여부와 무관하게 항상 동작하지만, n이 크면 느려요. "정렬 안 된 데이터에서 한 번만 찾을 때"엔 오히려 이게 정답이에요.
이진 탐색(binary search)은 정렬된 배열에서 가운데 값과 찾는 값을 비교해, 한쪽 절반을 통째로 버리며 범위를 절반씩 줄여 찾는 방법이에요 → O(log n). 전제는 딱 하나, 배열이 반드시 정렬돼 있어야 함이에요.
low(왼쪽), high(오른쪽)를 잡아요.mid를 구해 arr[mid]와 target을 비교해요.function binarySearch(arr, target) { // arr은 오름차순 정렬 가정
let low = 0, high = arr.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2); // 가운데 인덱스
if (arr[mid] === target) {
return mid; // 찾음
} else if (arr[mid] < target) {
low = mid + 1; // 오른쪽 절반만 남김
} else {
high = mid - 1; // 왼쪽 절반만 남김
}
}
return -1; // 없음
}
binarySearch([1, 3, 5, 7, 9, 11], 9); // → 4
💡 한 번 비교할 때마다 후보가 절반으로 줄어요. 그래서 아무리 커도 몇 번이면 끝나요.
데이터가 n = 1,000,000개일 때, 선형 탐색은 최악의 경우 100만 번 비교해요. 이진 탐색은 매번 절반으로 줄어드니 2²⁰ ≈ 100만, 즉 약 20번이면 끝나요.
| 데이터 개수 n | 선형 탐색 (최악) | 이진 탐색 (최악) |
|---|---|---|
| 1,000 | 약 1,000번 | 약 10번 |
| 1,000,000 | 약 100만 번 | 약 20번 |
| 10억 | 약 10억 번 | 약 30번 |
| 복잡도 | O(n) | O(log n) |
이진 탐색이 항상 정답은 아니에요. 정렬돼 있지 않은 데이터라면 먼저 정렬(O(n log n))해야 하는데, 딱 한 번만 찾을 거라면 그냥 선형 탐색(O(n))이 더 빠를 수 있어요.
참고로 "탐색"에는 배열에서 값을 찾는 것 말고, 그래프나 트리처럼 연결된 구조를 훑는 탐색도 있어요. 뒤 챕터에서 자세히 배우지만, 아래 두 용어는 문제풀이에도 나오니 미리 감만 잡아둬요.