여러 개의 문자열이나 배열을 비교해야 할 때, 배열 간에 직접 일치 여부를 확인하지 않고 객체를 만들어 빈도수를 따로 세주기
숫자를 담은 두 개의 배열 arr1, arr2가 주어질 때, arr2에 있는 값들이 arr1의 제곱으로 이루어져 있으면 true, 아니면 false를 return 하시오.
두 개의 배열을 중첩된 루프를 통해 비교하는 방법
(for 안에 indexOf가 중첩되어 있음)
function same(arr1, arr2){
if(arr1.length !== arr2.length){
return false;
}
for(let i = 0; i < arr1.length; i++){
let correctIndex = arr2.indexOf(arr1[i] ** 2)
if(correctIndex === -1) {
return false;
}
console.log(arr2);
arr2.splice(correctIndex,1)
}
return true;
}
same([1,2,3,2], [9,1,4,4])
두 개의 배열을 객체로 세분화하여 각 배열의 요소를 분류하고 비교하는 방법
⭐️ 두 개의 루프(O(n))가 두 개의 중첩된 개별 루프(O(n^2))보다 성능이 훨씬 낫다는 점을 이용한다.
function same(arr1, arr2){
if(arr1.length !== arr2.length){
return false;
}
let frequencyCounter1 = {}
let frequencyCounter2 = {}
for(let val of arr1){
frequencyCounter1[val] = (frequencyCounter1[val] || 0) + 1
}
for(let val of arr2){
frequencyCounter2[val] = (frequencyCounter2[val] || 0) + 1
}
console.log(frequencyCounter1);
console.log(frequencyCounter2);
for(let key in frequencyCounter1){
if(!(key ** 2 in frequencyCounter2)){
return false
}
if(frequencyCounter2[key ** 2] !== frequencyCounter1[key]){
return false
}
}
return true
}
same([1,2,3,2,5], [9,1,4,4,11])
두 개의 문자열 str1, str2가 주어질 때, str2가 str1의 anagram인지 판별하기.(str2를 조합해서 str2를 만들 수 있는가)
조건 : Frequency Counter 방식으로 풀기
각각의 counter를 만들고 빈도수를 세서 비교해준다.
function validAnagram(str1,str2){
// 길이가 다른 경우 false, 둘 다 빈 문자열이면 true
if(str1.length !== str2.length) return false;
if(!str1 && !str2) return true;
// 각각 counter 만들기
let counter1 = {};
let counter2 = {};
// 빈도수 구하기
for(let n of str1){
counter1[n] = (counter1[n] || 0) + 1;
}
for(let n of str2){
counter2[n] = (counter2[n] || 0) + 1;
}
// 비교하기
for(let key in counter1){
if(counter1[key] !== counter2[key]){
return false;
}
}
return true;
}
validAnagram('', '') // true
validAnagram('aaz', 'zza') // false
validAnagram('anagram', 'nagaram') // true
counter를 하나만 만들어서 str1을 먼저 넣고, 그 후에 str2의 존재여부를 확인하는 방법도 있다.
(좀 더 단순해짐)
function validAnagram(first, second) {
if (first.length !== second.length) {
return false;
}
const lookup = {};
for (let i = 0; i < first.length; i++) {
let letter = first[i];
// if letter exists, increment, otherwise set to 1
lookup[letter] ? lookup[letter] += 1 : lookup[letter] = 1;
}
for (let i = 0; i < second.length; i++) {
let letter = second[i];
// can't find letter or letter is zero then it's not an anagram
if (!lookup[letter]) {
return false;
} else {
lookup[letter] -= 1;
}
}
return true;
}
validAnagram('anagrams', 'nagaramm')
배열에서 여러개의 포인터를 이용해 특정 방향으로 이동하며 답 찾기
정렬된 배열이 주어질 때, 처음으로 합이 0이 되는 쌍을 찾아서 반환하기.
(존재하지 않으면 undefined 반환하기)
중첩된 for문으로 배열을 돌며 하나씩 전부 체크하기
function sumZero(arr){
for(let i = 0; i < arr.length; i++){
for(let j = i+1; j < arr.length; j++){
if(arr[i] + arr[j] === 0){
return [arr[i], arr[j]];
}
}
}
}
sumZero([-4,-3,-2,-1,0,1,2,5])
가장 작은 값(왼쪽)과 가장 큰 값(오른쪽)을 더해서 0이 되는 경우 찾기
function sumZero(arr){
let left = 0;
let right = arr.length -1 ;
while(left < right){
let sum = arr[left] + arr[right];
if(sum === 0) {
return [arr[left], arr[right]]
}else if(sum > 0){
right--;
}else{
left++:
}
}
}
sumZero([-4,-3,-2,-1,0,1,2,5])
정렬된 배열을 입력 받아서 unique value의 갯수를 return 하기
i
는 0부터 시작, j
는 1부터 시작)i
가 unique value의 갯수와 같으므로 i를 return한다.⭐️ 여기서는 위와 다르게 두 포인터가 같은 방향으로 이동하는 것이 포인트
function countUniqueValues(arr){
let i = 0;
let j = 1;
while(j<=arr.length){
if(arr[i] === arr[j]){
j++;
}else{
arr[i+1] = arr[j];
i++;
j++;
}
}
return i;
}
countUniqueValues([1,1,1,1,1,2]) // 2
countUniqueValues([1,2,3,4,4,4,7,7,12,12,13]) // 7
countUniqueValues([]) // 0
countUniqueValues([-2,-1,-1,0,1]) // 4
반복문을 통해 j
를 증가시키며 이동하면 더 짧게 나타낼 수 있다.
function countUniqueValues(arr){
if(arr.length === 0) return 0;
var i = 0;
for(var j = 1; j < arr.length; j++){
if(arr[i] !== arr[j]){
i++;
arr[i] = arr[j]
}
}
return i + 1;
}
countUniqueValues([1,2,2,5,7,7,99])
연속적인 데이터의 하위 집합을 찾는데 유용함.
(특정 갯수만큼 묶어서 이동시키기)
입력값으로 배열 arr
와 숫자 num
이 주어질 때, arr
을 num
개씩 합할 때의 최댓값을 구하시오.
중첩된 반복문을 이용해 i
만큼씩 이동하며 j
개씩 더하여 더 큰 값을 반환함.
ex. maxSubarraySum([2,6,9,2,1,8,5,6,3],3)
2+6+9
6+9+2
9+2+1
...
주어진 배열과 숫자의 크기가 매우 클 때, 값을 새롭게 계속 더하며 비교하는 방식은 매우 비효율적일 수 있음.
시간 복잡도 : O(n^2)
function maxSubarraySum(arr, num) {
if (num > arr.length){
return null;
}
var max = -Infinity;
for (let i = 0; i < arr.length - num + 1; i ++){
temp = 0;
for (let j = 0; j < num; j++){
temp += arr[i + j];
}
if (temp > max) {
max = temp;
}
}
return max;
}
maxSubarraySum([2,6,9,2,1,8,5,6,3],3)
처음부터 끝까지 num개의 숫자를 계속 새롭게 더하는 것이 아닌, 처음 num개의 합계를 유지한 상태로
앞에 숫자를 하나 빼고 뒤에 숫자를 하나 더하며 이동함.
ex. maxSubarraySum([2,6,9,2,1,8,5,6,3],3)
기본값 : (2+6+9)
(2
+6+9) -2
+2
= 6+9+2
(6
+9+2) -6
+1
= 9+2+1
(9
+2+1) -9
+8
= 2+1+8
...
숫자를 계속 새롭게 더하지 않고, 기본 합계에서 앞,뒤 하나씩만 제어하면서 합계를 구할 수 있게 되어 매우 효율적이다.
반복문을 중첩해서 사용하지 않아도 되기 때문에 시간 복잡도도 단순해진다.
시간 복잡도 : O(n)
function maxSubarraySum(arr, num){
let maxSum = 0;
let tempSum = 0;
if (arr.length < num) return null;
for (let i = 0; i < num; i++) {
maxSum += arr[i];
}
tempSum = maxSum;
for (let i = num; i < arr.length; i++) {
tempSum = tempSum - arr[i - num] + arr[i];
maxSum = Math.max(maxSum, tempSum);
}
return maxSum;
}
maxSubarraySum([2,6,9,2,1,8,5,6,3],3)
실제 사용하는 문서화된 이름이다.
분할 정복 알고리즘 : 이진 탐색, 병합 정렬, 퀵 정렬 등
정렬된 배열과 특정 숫자가 주어질 때, 배열에서 해당 숫자의 index를 반환하시오.
선형 탐색
이진 탐색