
미국의 주들 중 원하는 주를 찾으려면 어떻게 해야 할까?
가장 단순한 방법은 모든 주를 하나씩 순서대로 확인하면서
내가 찾는 주인지 비교해보는 것이다.
이 방식을 선형 검색(Linear Search) 이라고 한다.
JavaScript에서는 우리가 직접 반복문을 돌리지 않아도,
선형 검색을 내부적으로 수행하는 메서드들이 이미 제공된다.
['CA', 'NY', 'TX'].indexOf('NY'); // 1
['CA', 'NY', 'TX'].indexOf('FL'); // -1
['CA', 'NY', 'TX'].includes('TX'); // true
['CA', 'NY', 'TX'].includes('FL'); // false
const users = [
{ name: 'kim', age: 20 },
{ name: 'lee', age: 30 }
];
users.find(user => user.age === 30);
// { name: 'lee', age: 30 }
users.findIndex(user => user.age === 30); // 1
이 메서드들 모두 배열의 처음부터 끝까지 하나씩 확인한다는 점에서 선형 검색과 동일한 시간 복잡도(O(n)) 를 가진다.
선형 검색의 시간 복잡도는 O(n) 이다.
만약 수억, 수조 번의 탐색을 선형 검색으로 처리해야 한다면
성능 문제가 생길 수 있다.
이런 상황에서 등장하는 것이 이진 검색(Binary Search) 이다.
정렬된 배열이어야 한다.
function binarySearch(arr, num) {
arr.sort((a, b) => a - b);
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const middle = Math.floor((left + right) / 2);
if (num < arr[middle]) {
right = middle - 1;
} else if (num > arr[middle]) {
left = middle + 1;
} else {
return middle;
}
}
return -1;
}
핵심 포인트
이진 검색은 값 비교보다 인덱스 관리가 핵심이다.
left, right 인덱스를 기준으로 탐색 범위를 계속 좁혀가는 구조
O(log n)
탐색할 때마다 확인 범위가 절반으로 줄어든다.
데이터가 많을수록 선형 검색과의 성능 차이는 극명해진다.
"wowomgzomg" 안에 "omg"가 포함되어 있는지를 확인한다고 해보자.
가장 단순한 방법은
긴 문자열을 한 글자씩 이동하며 "omg"의 첫 글자부터 비교하는 것이다.
이 방식을 나이브 문자열 검색이라고 한다.
function naiveSearch(long, short) {
let count = 0;
for (let i = 0; i <= long.length - short.length; i++) {
let match = true;
for (let j = 0; j < short.length; j++) {
if (long[i + j] !== short[j]) {
match = false;
break;
}
}
if (match) count++;
}
return count;
}
O(n × m)
n: 긴 문자열 길이
m: 찾는 문자열 길이
문자열이 길어질수록, 패턴이 길어질수록 성능이 급격히 떨어진다.