Code/Codility

4. 원소 개수 세기 (Counting Elements) Codility Lesson 한국어 정리본 (JavaScript ver.)

haeunkim.on 2025. 3. 1. 23:19

원소 개수 세기란?

배열을 활용하여 숫자 시퀀스를 저장하는 방법에는 여러 가지가 있다. 일반적으로 연속된 숫자 a₀, a₁, ..., aₙ₋₁를 배열의 인덱스에 맞춰 저장한다.

예를 들어:

let A = [4, 2, 4, 5];
// A[0] = 4, A[1] = 2, A[2] = 4, A[3] = 5

그러나 원소를 직접 저장하는 대신, 각 숫자의 등장 횟수를 세는 배열 을 만들 수도 있다.

이 경우, 배열 count[] 는 해당 값이 등장한 횟수를 저장한다.

count[2] = 1; // 숫자 2는 한 번 등장
count[4] = 2; // 숫자 4는 두 번 등장
count[5] = 1; // 숫자 5는 한 번 등장

이 방법을 사용하면 특정 숫자가 등장한 횟수를 O(1) 시간에 확인할 수 있다.


1. O(n + m) 시간 복잡도의 원소 개수 세기

다음 함수는 배열 A에 등장하는 각 숫자의 개수를 세는 함수이다.

JavaScript 코드:

function counting(A, m) {
    let n = A.length;
    let count = new Array(m + 1).fill(0);
    for (let k = 0; k < n; k++) {
        count[A[k]] += 1;
    }
    return count;
}

이 함수는 O(n + m) 시간 복잡도를 가진다. m 값이 너무 크면 메모리 사용량이 증가할 수 있다.


2. 배열 교환을 통한 합 일치 여부 확인

배열 A와 B가 주어졌을 때, 하나의 원소를 교환하여 두 배열의 합을 동일하게 만들 수 있는지 확인하는 문제이다.

O(n²) (비효율적인 방법)

function slowSolution(A, B, m) {
    let n = A.length;
    let sumA = A.reduce((a, b) => a + b, 0);
    let sumB = B.reduce((a, b) => a + b, 0);
    
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            let change = B[j] - A[i];
            sumA += change;
            sumB -= change;
            
            if (sumA === sumB) return true;
            
            sumA -= change;
            sumB += change;
        }
    }
    return false;
}

이 방법은 모든 가능한 쌍을 교환해보므로 O(n²) 시간이 걸린다.


O(n + m) (효율적인 방법)

좀 더 효율적인 방법은 원소 개수를 세는 배열을 활용하는 것이다.

function fastSolution(A, B, m) {
    let n = A.length;
    let sumA = A.reduce((a, b) => a + b, 0);
    let sumB = B.reduce((a, b) => a + b, 0);
    let d = sumB - sumA;
    
    if (d % 2 !== 0) return false;
    d /= 2;
    
    let count = counting(A, m);
    
    for (let i = 0; i < n; i++) {
        if (B[i] - d >= 0 && B[i] - d <= m && count[B[i] - d] > 0) {
            return true;
        }
    }
    return false;
}

이 방법은 O(n + m) 시간 복잡도를 가지며, 더 큰 입력 값에도 효과적으로 실행될 수 있다.

반응형