두 개의 배열(base, sample)을 입력받아 sample이 base의 부분집합인지 여부를 리턴해야 합니다.
let base = [1, 2, 3, 4, 5];
let sample = [1, 3];
let output = isSubsetOf(base, sample);
console.log(output); // --> true
처음 이 문제를 풀었을 때 base, sample 배열을 for문으로 하나씩 돌며 sample의 element를 모두 포함하고 있는지 확인하였다.
하지만 이렇게 풀게 되면 시간 복잡도는 O(M*N)이 된다.
const isSubsetOf = function (base, sample) {
for (let i = 0; i < sample.length; i++) {
if (!base.includes(sample[i])) {
return false;
}
}
return true;
};
실행 시간 초과 오류 발생
const isSubsetOf = function (base, sample) {
base.sort((a, b) => a - b);
sample.sort((a, b) => a - b);
let result = true;
for (let i = 0; i < sample.length; i++) {
if (result === false) {
return result;
}
for (let j = i; j < base.length; j++) {
if (sample[i] === base[j]) {
result = true;
break;
} else {
result = false;
}
}
}
return result;
};
테스트는 통과했지만, 불필요한 반복이 발생하고 있다.
시간 복잡도를 개선하기 위해 이전에 프로그래머스를 풀면서 학습한 이분탐색을 적용해 보았다.
const isSubsetOf = function (base, sample) {
base.sort((a, b) => a - b);
let arr = Array(sample.length).fill(false);
for (let i = 0; i < sample.length; i++) {
let max = base.length - 1;
let min = 0;
while (min <= max) {
let midIdx = Math.floor((max + min) / 2);
if (base[midIdx] === sample[i]) {
arr[i] = true;
break;
}
if (sample[i] >= base[midIdx]) {
min = midIdx + 1;
} else {
max = midIdx - 1;
}
}
}
return !arr.includes(false);
};
위의 코드는 중간값과의 크기비교를 통해 필요한 탐색 횟수를 줄여주어 시간 복잡도를 개선시킨다.
const isSubsetOf = function (base, sample) {
base.sort((a, b) => a - b);
sample.sort((a, b) => a - b);
let result = true;
for (let i = 0; i < sample.length; i++) {
if (result === false) {
return result;
}
for (let j = i; j < base.length; j++) {
if (sample[i] === base[j]) {
result = true;
break;
} else {
result = false;
}
}
}
return result;
};
해당 문제의 최종적인 의도는 불필요한 반복을 제거하는 것이다.
이를 위해서 sample의 요소를 찾은 인덱스를 저장하고, 다음 요소를 찾을 때에는 찾은 인덱스 이후의 인덱스만 탐색하면 되기 때문에, 불필요한 반복이 줄어들게 된다.
빅오 표기법
알고리즘의 성능을 표기하는 것으로, 알고리즘의 실제 러닝타임이 아닌 데이터의 증감에 따른 알고리즘의 성능을 예측한다.
상수는 1이 된다.