자바스크립트 코딩테스트 강좌, 소수 구하기

안녕하세요! 오늘은 자바스크립트 코딩 테스트에서 자주 출제되는 ‘소수 구하기’ 문제에 대해 알아보겠습니다. 소수는 1과 자기 자신 외에 아무 정수로도 나눠 떨어지지 않는 자연수를 의미합니다. 예를 들어, 2, 3, 5, 7, 11은 모두 소수입니다. 이 글에서는 소수를 찾기 위한 알고리즘을 구현하고 그 과정을 자세히 설명드리겠습니다.

1. 문제 정의

주어진 정수 N이 있을 때, N 이하의 모든 소수를 출력하는 프로그램을 작성하시오.

입력: 정수 N (1 ≤ N ≤ 10,000)

출력: N 이하의 모든 소수를 한 줄에 출력

2. 알고리즘 선택

소수를 구하기 위해 사용될 대표적인 알고리즘은 에라토스테네스의 체(Sieve of Eratosthenes)입니다. 이 알고리즘은 매우 효율적이며, N 이하의 소수를 찾는 데 적합합니다. 알고리즘의 사용 이유는 다음과 같습니다:

  • O(N log log N)의 시간 복잡도로 매우 빠릅니다.
  • 메모리 사용량이 적당하여 N이 10,000까지는 무난하게 처리할 수 있습니다.

2.1 에라토스테네스의 체 알고리즘 설명

에라토스테네스의 체 알고리즘은 다음과 같은 단계로 진행됩니다:

  1. 2부터 N까지의 모든 수를 리스트에 추가합니다.
  2. 리스트의 첫 번째 숫자인 2를 소수로 확정합니다. 2의 배수는 소수가 아니므로 리스트에서 제거합니다.
  3. 다음 수(3)를 소수로 확정하고, 3의 배수를 제거합니다. 이 과정을 반복합니다.
  4. 현재 수가 N의 제곱근보다 작거나 같은 경우에만 반복합니다.

3. 코드 작성

이제 위 알고리즘을 기반으로 자바스크립트로 코드를 작성해 보겠습니다.

function sieveOfEratosthenes(N) {
    const primes = [];
    const isPrime = new Array(N + 1).fill(true);
    isPrime[0] = isPrime[1] = false; // 0과 1은 소수가 아닙니다.

    for (let i = 2; i <= N; i++) {
        if (isPrime[i]) {
            primes.push(i); // i는 소수입니다.
            for (let j = i * 2; j <= N; j += i) {
                isPrime[j] = false; // i의 배수는 소수가 아닙니다.
            }
        }
    }
    return primes;
}

// 사용 예시
const N = 100; // N을 입력받습니다.
const primeNumbers = sieveOfEratosthenes(N);
console.log(primeNumbers.join(' ')); // 소수를 출력합니다.

4. 코드 분석

작성된 코드를 하나씩 살펴보겠습니다:

  • const isPrime = new Array(N + 1).fill(true);: N까지의 숫자 배열을 생성하고 모든 값을 true로 초기화합니다.
  • isPrime[0] = isPrime[1] = false;: 0과 1은 소수가 아니므로 false로 설정합니다.
  • for 루프를 통해 2부터 N까지의 수를 검사합니다. 만약 isPrime[i]가 true라면, 이는 i가 소수임을 의미합니다. 이 숫자를 primes 배열에 추가합니다.
  • 또한, i의 모든 배수를 반복하여 false로 설정합니다.
  • 이 과정을 통해 최종적으로 소수만 남은 배열을 반환합니다.

5. 테스트 케이스

이제 우리의 구현이 잘 작동하는지 확인하기 위해 몇 가지 테스트 케이스를 실행해 보겠습니다.

console.log(sieveOfEratosthenes(10)); // [2, 3, 5, 7]
console.log(sieveOfEratosthenes(50)); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
console.log(sieveOfEratosthenes(100)); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

6. 결론

오늘은 자바스크립트에서 소수를 구하는 방법에 대해 알아보았습니다. 에라토스테네스의 체를 활용하여 효율적으로 소수를 찾아낼 수 있는 방법을 살펴보았고, 이를 바탕으로 실용적인 코드를 작성하였습니다. 이 코드를 통해 여러분의 알고리즘 실력을 한층 더 발전시키시길 바랍니다. 더불어, 코딩 테스트 준비에 많은 도움이 되기를 바랍니다!

7. 추가 학습 자료

더 많은 자료와 문제를 해결하고 싶다면 다음의 리소스를 참고하세요:

  • LeetCode – 다양한 알고리즘 문제와 해설
  • HackerRank – 코딩 테스트 문제 풀이 및 연습
  • Codewars – 다양한 언어로 문제를 해결하며 코딩 연습

자바스크립트 코딩테스트 강좌, 블루레이 만들기

안녕하세요! 이번 블로그 글에서는 자바스크립트 코딩테스트를 대비하기 위한 알고리즘 문제를 다뤄보겠습니다. 주제는 ‘블루레이 만들기’입니다. 이 문제는 주어진 영화를 블루레이로 만들 때 필요한 최소의 블루레이 장수를 구하는 것입니다. 이를 해결하기 위해서는 이분 탐색과 그리디 알고리즘을 활용해야 할 것입니다.

문제 설명

당신은 여러 영화를 블루레이로 만들고 싶습니다. 각 영화의 상영 시간이 주어지며, 블루레이에는 최대 K만큼의 시간을 담을 수 있습니다. 당신의 목표는 블루레이의 수를 최소화하여 모든 영화를 블루레이에 담는 것입니다. 단, 각 블루레이에는 상영 시간이 K를 넘지 않도록 영화들을 배분해야 합니다.

입력

  • 첫 줄에는 N과 K가 주어집니다. (1 ≤ N ≤ 1000, 1 ≤ K ≤ 10^6)
  • 두 번째 줄에는 N개의 자연수가 공백으로 구분되어 각 영화의 상영 시간이 주어집니다. (1 ≤ 영화의 상영 시간 ≤ 10^6)

출력

블루레이의 최소 개수를 출력합니다.

예시

입력:
4 5
2 3 1 4

출력:
2

문제 풀이 과정

이 문제를 접근하기 위해서는 다음의 단계로 진행할 수 있습니다.

1단계: 문제 이해

주어진 영화를 최대 K의 상영 시간을 갖는 블루레이에 어떻게 배분할 것인가를 고민합니다. 각 영화의 상영 시간이 주어졌고, 이 시간을 합쳐 K를 넘지 않는 범위 내에서 블루레이에 담아야 하므로, 가능한 경우의 수를 고려해야 합니다.

2단계: 아이디어 도출

모든 영화를 단순하게 하나의 블루레이에 담을 수는 없으므로, 영화 리스트를 반복적으로 탐색하면서 각 블루레이에 담아도 되는지를 확인합니다. 이를 위해서 이분 탐색을 활용하여 블루레이의 최소 개수를 찾는 방법을 사용할 것입니다.

3단계: 예외 처리

각 영화를 담는 시간이 최대 K를 초과하면, 해당 영화를 새로운 블루레이에 담아야 합니다. 이 조건을 주의하여 최대한 많은 영화를 블루레이에 담는 것이 중요합니다.

4단계: 알고리즘 구현

이제 위의 아이디어를 기반으로 자바스크립트 함수를 구현하겠습니다.


function minBluRays(N, K, movies) {
    let bluRays = 0;
    let currentTime = 0;

    for (let i = 0; i < N; i++) {
        if (currentTime + movies[i] > K) {
            bluRays++;
            currentTime = movies[i];
        } else {
            currentTime += movies[i];
        }
    }

    if (currentTime > 0) {
        bluRays++;
    }

    return bluRays;
}

// 예제 실행
const N = 4;
const K = 5;
const movies = [2, 3, 1, 4];
console.log(minBluRays(N, K, movies)); // 출력: 2

결론

이번 글에서는 ‘블루레이 만들기’ 문제를 통해 자바스크립트 코딩테스트에서 자주 등장하는 알고리즘 문제를 풀이하는 과정을 보여주었습니다. 문제의 본질을 이해하고, 필요한 알고리즘을 찾는 것이 매우 중요합니다. 주어진 시간을 잘 활용하여 효율적인 코드를 작성하는 것이 좋습니다.

이 문제를 통해 자바스크립트의 기본적인 사용법과 알고리즘적인 사고 방식을 개발할 수 있기를 바랍니다. 감사합니다!

자바스크립트 코딩테스트 강좌, 가장 큰 정사각형 찾기

안녕하세요! 이번 글에서는 “가장 큰 정사각형 찾기” 문제를 JavaScript로 해결하는 방법을 자세히 알아보겠습니다. 이 문제는 동적 프로그래밍(Dynamic Programming) 접근 방식을 활용하여 효율적으로 해결할 수 있습니다. 실제 기업의 코딩 테스트에서 자주 출제되는 유형이므로, 이 문제를 통해 동적 프로그래밍의 개념을 확고히 하고, 실전에서의 활용 방법에 대해 알아보도록 하겠습니다.

문제 설명

주어진 2차원 배열 matrix가 있을 때, ‘1’로 이루어진 가장 큰 정사각형의 변의 길이를 구하는 문제입니다. 예를 들어, 아래와 같은 입력 행렬이 주어진다면:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

위 행렬에서 가장 큰 정사각형의 변의 길이는 2입니다. 즉, ‘1’로 이루어진 최대 크기는 2×2입니다.

문제 해결 접근

이 문제는 여러 가지 방법으로 접근할 수 있지만, 동적 프로그래밍을 사용하는 것이 가장 효율적입니다. 동적 프로그래밍을 사용하면 이전에 계산한 값을 재사용하여 불필요한 계산을 줄일 수 있습니다. 다음은 이 문제를 해결하기 위한 단계별 접근 방법입니다.

1단계: 동적 프로그래밍 테이블 설정

먼저, 기존 행렬과 동일한 크기의 2차원 배열인 dp를 생성합니다. 이 배열은 각 위치에 ‘1’로 이루어진 정사각형의 최대 변의 길이를 저장할 것입니다.

2단계: 경계 조건 처리

행렬의 첫 번째 행과 첫 번째 열은 경계 조건으로 설정합니다. 이 경우, 해당 위치가 ‘1’이라면 최대 정사각형의 변 길이는 1입니다. 만약 ‘0’이라면 어디에도 정사각형이 존재하지 않으므로 0으로 설정합니다.

3단계: DP 배열 채우기

이후, 행렬을 순회하면서 각 위치의 값에 따라 DP 배열을 업데이트합니다. 현재 위치 matrix[i][j]가 ‘1’이라면, 해당 위치에서 정사각형의 변의 길이를 다음과 같이 구합니다:

dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;

여기서 Math.min() 함수는 위쪽, 왼쪽, 그리고 왼쪽 대각선에서 최소값을 구하는 함수입니다. 이 최소값은 정사각형을 만들기 위해 필요한 최소한의 길이를 나타냅니다.

4단계: 결과 처리

마지막으로, DP 배열에서 가장 큰 값을 찾으면 그 값이 정사각형의 변의 길이가 됩니다.

알고리즘 구현

이제 앞서 설명한 접근 방식을 바탕으로 실제 알고리즘을 JavaScript로 구현해보겠습니다.

function maximalSquare(matrix) {
    if (matrix.length === 0) return 0;

    const rows = matrix.length;
    const cols = matrix[0].length;
    const dp = Array.from({ length: rows }, () => Array(cols).fill(0));
    let maxSide = 0;

    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (matrix[i][j] === '1') {
                if (i === 0 || j === 0) {
                    dp[i][j] = 1; // 첫 번째 열이나 행일 경우는 1
                } else {
                    dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
                }
                maxSide = Math.max(maxSide, dp[i][j]);
            }
        }
    }

    return maxSide * maxSide; // 정사각형의 넓이 반환
}

성능 및 복잡도 분석

이 알고리즘의 시간 복잡도는 O(m*n)이며, 공간 복잡도는 O(m*n)입니다. 여기서 m은 행렬의 행 수, n은 열 수입니다. DP 배열을 사용하기 때문에 공간이 추가로 필요하지만, 이를 최적화하여 O(n)으로 줄일 수 있습니다.

최적화: 공간 복잡도 감소

경우에 따라 DP 배열의 공간 복잡도를 O(n)으로 줄일 수 있습니다. 이전 DP 배열을 저장하기 위해 두 개의 배열을 사용할 수 있으며, 현재 상태를 업데이트할 때는 이전 상태만 필요합니다.

function maximalSquare(matrix) {
    if (matrix.length === 0) return 0;

    const rows = matrix.length;
    const cols = matrix[0].length;
    let dp = Array(cols).fill(0);
    let maxSide = 0, prev = 0;

    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            let temp = dp[j]; // 이전 값을 저장
            if (matrix[i][j] === '1') {
                if (i === 0 || j === 0) {
                    dp[j] = 1;
                } else {
                    dp[j] = Math.min(dp[j], dp[j-1], prev) + 1;
                }
                maxSide = Math.max(maxSide, dp[j]);
            } else {
                dp[j] = 0; // '0'일 경우 0 설정
            }
            prev = temp; // 이전 값을 업데이트
        }
    }

    return maxSide * maxSide; // 정사각형의 넓이 반환
}

마치며

오늘은 자바스크립트 코딩테스트에서 자주 등장하는 “가장 큰 정사각형 찾기” 문제를 동적 프로그래밍을 통해 해결하는 방법을 알아보았습니다. 이 문제는 동적 프로그래밍의 기본 개념을 이해하는 데 도움을 주며, 실전에서의 코드 작성 능력을 향상시키는 데 큰 도움이 될 것입니다. 다양한 문제를 풀어보며 알고리즘과 데이터 구조에 대한 이해도를 높이는 것도 잊지 마세요!

앞으로 더 많은 알고리즘 문제를 다룰 예정이니 많은 관심 부탁드립니다. 감사합니다!

자바스크립트 코딩테스트 강좌, 시간 복잡도 활용하기

문제 설명

주어진 배열 arr에서 특정 num 값과 가장 가까운 수를 찾는 문제입니다.
가장 가까운 수가 여러 개 있을 경우, num에 가까운 값이 더 낮을 경우를 우선합니다.

입력

  • 정수 배열 arr (-106arr[i] ≤ 106, 1 ≤ arr.length ≤ 105)
  • 정수 num (-106num ≤ 106)

출력

가장 가까운 수를 반환합니다.

예제

예제 1:
입력: arr = [1, 2, 3, 4, 5], num = 3
출력: 3

예제 2:
입력: arr = [1, 2, 4, 5], num = 3
출력: 2

예제 3:
입력: arr = [5, 10, 15], num = 12
출력: 10

풀이 과정

이 문제를 해결하기 위해서 다음과 같은 단계를 따릅니다.

1단계: 문제 이해하기

우선 num과 가장 가까운 수를 찾는 문제이므로, 각 요소와 num의 차이인 절댓값을 계산하여 가장 작은 값을 찾는 것이 핵심입니다. 이때, 같은 차이 값을 가진 여러 개의 수가 있을 경우 더 작은 값을 반환해야 합니다.

2단계: 접근 방법 선정하기

가장 간단한 방법은 배열을 순회하며 각 요소의 절댓값 차이를 계산하는 것입니다. 하지만 이 방식은 O(n) 시간복잡도를 가지므로, 이보다 빠른 알고리즘을 고려해야 합니다.

3단계: 정렬을 통한 이진 탐색 활용하기

입력 배열을 정렬하면 이진 탐색을 통해 num 값에 가장 가까운 수를 효율적으로 찾을 수 있습니다. 정렬된 배열에서 특정 값 num 의 인덱스를 찾기 위해 Array.prototype.sort()를 사용하고, 이에 이어서 binarySearch() 함수를 구현합니다.

4단계: 코드 구현하기

다음은 위에서 설명한 내용을 바탕으로 작성한 자바스크립트 코드입니다.


function findClosest(arr, num) {
    // 배열 정렬
    arr.sort((a, b) => a - b);
    
    let left = 0;
    let right = arr.length - 1;
    let closest = arr[0];

    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        
        // 절댓값 차이 비교
        if (Math.abs(arr[mid] - num) < Math.abs(closest - num) ||
            (Math.abs(arr[mid] - num) === Math.abs(closest - num) && arr[mid] < closest)) {
            closest = arr[mid];
        }
        
        // 이진 탐색의 방향 결정
        if (arr[mid] < num) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return closest;
}

// 예제 테스트
console.log(findClosest([1, 2, 3, 4, 5], 3)); // 3
console.log(findClosest([1, 2, 4, 5], 3));    // 2
console.log(findClosest([5, 10, 15], 12));      // 10

5단계: 시간 복잡도 분석

위 코드는 먼저 배열을 정렬합니다. 정렬의 시간 복잡도는 O(n log n)이며, 이후 이진 탐색을 이용한 탐색 과정에서는 O(log n)의 시간이 소요됩니다. 전체 시간 복잡도는 O(n log n)로 평가할 수 있습니다.

결론

이 문제를 통해 시간 복잡도를 고려하여 효율적인 알고리즘을 선택하는 것이 얼마나 중요한지를 배웠습니다. 주어진 문제에 대해 다양한 접근 방식을 시도하고, 그 결과에 따라 적절한 알고리즘을 선택하는 연습이 필요합니다. 앞으로의 코딩테스트에서도 이러한 원칙을 잘 활용하시길 바랍니다!

자바스크립트 코딩테스트 강좌, 깊이 우선 탐색

문제: 미로 탐색

주어진 2차원 배열에서 출발점 (시작점)에서 도착점까지의 경로가 존재하는지를 확인하는 문제입니다. 배열의 값은 0 (통과 가능)과 1 (통과 불가능)으로 주어집니다. 출발점은 (0, 0)이고, 도착점은 (N-1, M-1)입니다. 상하좌우로만 이동할 수 있으며, 경로가 존재하면 true, 존재하지 않으면 false를 반환해야 합니다.

문제 예시

입력

    [
        [0, 0, 1, 0],
        [1, 0, 1, 0],
        [0, 0, 0, 0],
        [0, 1, 1, 0]
    ]
    

출력

true

설명

위의 예시에서 출발점 (0, 0)에서 시작하여 (1, 1), (2, 1), (2, 2), (2, 3), (3, 3)으로 경로를 따라 가면 도착점 (3, 3)에 도달할 수 있습니다.

문제 해결 과정

1. 알고리즘의 개요

이 문제는 깊이 우선 탐색(DFS) 알고리즘을 통해 해결할 수 있습니다. DFS는 노드를 방문할 때 가능한 한 깊게 들어갔다가 더 이상 갈 수 없게 되면 다른 경로로 돌아오는 방식입니다. 우리는 재귀를 사용하여 구현할 것입니다. DFS를 적용하여 미로를 탐색하고, 출발점에서 도착점까지 도달 가능한지를 확인합니다.

2. DFS 알고리즘 구현

DFS를 구현하기 위해서는 다음과 같은 단계가 필요합니다:

  1. 현재 위치가 경계 내에 있는지 확인합니다.
  2. 현재 위치가 통과 불가능한 지점인지 확인합니다.
  3. 현재 위치가 도착지인지 확인합니다.
  4. 현재 위치를 방문 처리합니다.
  5. 상하좌우 방향으로 재귀적으로 DFS를 호출합니다.
  6. 모든 경로를 탐색한 후 방문 처리를 해제합니다.

3. 코드 구현

다음은 자바스크립트를 이용한 DFS 알고리즘을 구현한 코드입니다:


function isPathExists(maze) {
    const rows = maze.length;
    const cols = maze[0].length;

    function dfs(x, y) {
        // 경계 조건
        if (x < 0 || y < 0 || x >= rows || y >= cols) return false;
        // 통과 불가능한 지점
        if (maze[x][y] === 1) return false;
        // 도착지
        if (x === rows - 1 && y === cols - 1) return true;

        // 현재 위치 방문 처리
        maze[x][y] = 1;

        // 상하좌우 DFS 탐색
        const found = dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1);

        // 방문 처리 해제
        maze[x][y] = 0;

        return found;
    }

    return dfs(0, 0);
}

// 예제 테스트
const maze = [
    [0, 0, 1, 0],
    [1, 0, 1, 0],
    [0, 0, 0, 0],
    [0, 1, 1, 0]
];

console.log(isPathExists(maze)); // 출력: true

4. 코드 설명

코드의 주요 부분을 설명하겠습니다:

  • 경계 조건 체크: 현재 위치가 배열의 경계를 초과하지 않는지 확인합니다.
  • 통과 불가능한 지점 검사: 현재 위치의 값이 1인지 확인하여 통과 가능한 지점인지 검사합니다.
  • 도착지 도달: 현재 위치가 도착점 (N-1, M-1)이라면 true를 반환합니다.
  • 방문 처리: 현재 위치를 방문 처리하여 중복 탐색을 방지합니다.
  • 재귀 호출: 상하좌우 방향으로 DFS를 재귀적으로 호출하여 경로를 탐색합니다.
  • 방문 해제: 탐색이 끝난 후 방문 처리를 해제합니다.

5. 시간 복잡도 분석

이 알고리즘의 시간 복잡도는 O(N * M)입니다. 여기서 N은 행의 수, M은 열의 수입니다. 모든 셀을 한 번씩 방문할 수 있기 때문입니다. 하지만 재귀에 따른 스택 공간 오버헤드로 인해 최악의 경우 필요할 수 있는 추가 메모리도 존재합니다.

결론

이번 강좌에서는 깊이 우선 탐색(DFS)을 통해 미로 탐색 문제를 해결하는 방법을 배웠습니다. DFS는 복잡한 그래프 구조에서도 유용하게 사용될 수 있으며, 다양한 문제에 적용할 수 있습니다. DFS의 특징과 동작 방식을 이해했다면, 다양한 문제에 응용해보는 것이 좋습니다. 다음 강좌에서는 너비 우선 탐색(BFS) 알고리즘에 대해 알아보겠습니다. 이 강좌가 도움이 되었기를 바랍니다.