자바스크립트 코딩테스트 강좌, ‘좋은 수’ 구하기

소개

프로그래밍 언어의 기본적인 이해를 바탕으로 알고리즘 문제를 해결하는 것은 코딩테스트에서 중요한 요소입니다. 특히 자바스크립트는 웹 개발 및 frontend 분야에서 필수적으로 사용되는 언어로, 많은 기업들이 자바스크립트를 활용한 문제를 출제하고 있습니다. 이번 강좌에서는 ‘좋은 수’라는 문제를 통해 자바스크립트의 활용성을 배우고, 알고리즘 문제 해결 과정을 자세히 설명하겠습니다.

문제 설명

‘좋은 수’ 문제는 주어진 수들 가운데 특정 조건을 만족하는 수를 찾는 문제입니다. 이 문제의 구체적인 내용은 다음과 같습니다:

문제: 양의 정수 배열이 주어질 때, 배열에서 중복을 제거하고, 10보다 작은 수를 모두 출력하는 함수를 작성하세요. 또한 남은 수들의 평균을 소수점 첫째 자리까지 구하여 반환하세요.

입력 및 출력 형식

입력: 양의 정수로 이루어진 배열 arr ([1, 2, 2, 3, 10, 5, 7, 15])
출력:
1. 중복을 제거한 수 중 10보다 작은 수의 리스트
2. 남은 수들의 평균 (소수점 첫째 자리까지)

예시

    입력: [1, 2, 2, 3, 10, 5, 7, 15]
    출력: 
    중복을 제거한 수 (10보다 작은 수): [1, 2, 3, 5, 7]
    평균: 3.6
    

문제 해결 접근 방법

문제를 해결하기 위해서는 배열의 중복을 제거하고, 필터링된 배열의 평균을 계산하는 단계를 따라야 합니다. 자바스크립트를 사용하여 이러한 작업을 수행하는 방법을 단계별로 살펴보겠습니다.

1단계: 중복 제거

배열에서 중복된 요소를 제거하기 위해 Set 객체를 사용할 수 있습니다. Set 객체는 자동으로 중복을 허용하지 않으므로, 이 객체를 이용하여 원하는 결과를 쉽게 얻을 수 있습니다.


const arr = [1, 2, 2, 3, 10, 5, 7, 15];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 10, 5, 7, 15]
    

2단계: 필터링

이제 중복이 제거된 배열에서 10보다 작은 수를 필터링하는 작업을 수행하겠습니다. JavaScript의 filter() 메소드를 활용하여 조건에 맞는 요소만을 추출할 수 있습니다.


const filteredArr = uniqueArr.filter(num => num < 10);
console.log(filteredArr); // [1, 2, 3, 5, 7]
    

3단계: 평균 계산

필터링된 배열의 평균을 계산하기 위해서는 reduce() 메소드를 사용할 수 있습니다. 배열의 모든 요소를 더한 후, 그 총합을 요소의 개수로 나눈 값을 구하면 평균을 얻을 수 있습니다.


const average = filteredArr.reduce((acc, num) => acc + num, 0) / filteredArr.length;
console.log(average.toFixed(1)); // 3.6
    

전체 코드

지금까지 설명한 모든 과정을 하나의 함수로 통합하여 최종 코드를 작성하겠습니다.


function goodNumbers(arr) {
    const uniqueArr = [...new Set(arr)];
    const filteredArr = uniqueArr.filter(num => num < 10);
    const average = filteredArr.reduce((acc, num) => acc + num, 0) / filteredArr.length;
    return {
        filteredNumbers: filteredArr,
        average: average.toFixed(1)
    };
}

const inputArr = [1, 2, 2, 3, 10, 5, 7, 15];
const result = goodNumbers(inputArr);
console.log(result);
    

결과

위의 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.


{
    filteredNumbers: [1, 2, 3, 5, 7],
    average: "3.6"
}
    

최적화 및 고려할 점

위의 문제 해결 방법은 업무에 적합하게 잘 작동합니다. 하지만 대규모 데이터세트를 다루는 경우 성능에 대한 추가 고려가 필요할 수 있습니다. 예를 들어, Set을 사용하는 방법은 고유한 값을 쉽게 추출할 수 있지만, 배열의 크기가 매우 클 경우 메모리 사용량이 증가할 수 있습니다. 이러한 경우 알고리즘의 성능을 개선하기 위해 몇 가지 방법을 고려할 수 있습니다:

  • 중복 제거와 필터링을 동시에 수행하는 방법을 탐색하기.
  • 데이터 구조 선택에 따라 성능을 개선하기.

마무리

이번 강좌에서는 자바스크립트를 활용하여 ‘좋은 수’라는 문제를 해결하는 과정을 살펴보았습니다. 중복을 제거하고, 조건에 맞는 수를 필터링하며, 평균을 계산하는 일련의 단계를 통해 기본적인 알고리즘 문제 해결 능력을 향상시킬 수 있었습니다. 코딩테스트를 준비하는 과정에서 이러한 문제를 많이 풀어보며 연습하는 것이 중요합니다. 자바스크립트의 다양한 기능을 활용해 보며 더욱 깊이 있는 이해를 가져가시길 바랍니다.

도움이 되었나요?

이 강좌가 자바스크립트 코딩테스트를 준비하는 데 도움이 되었다면, 다른 알고리즘 문제도 도전해 보세요. 그 과정에서 발생하는 어려움이나 궁금한 점은 언제든지 댓글로 남겨주세요. 본인의 학습 여정을 공유하며 함께 성장할 수 있는 기회를 가지길 바랍니다.

자바스크립트 코딩테스트 강좌, 벨만-포드

안녕하세요. 오늘은 자바스크립트 코딩테스트를 준비하는 여러분을 위해 벨만-포드 알고리즘에 대해 자세히 알아보겠습니다. 벨만-포드 알고리즘은 그래프 이론에서 최단 경로를 찾기 위한 알고리즘 중 하나로, 특히 음의 가중치를 가진 간선이 있는 경우에도 적용할 수 있다는 장점이 있습니다. 이번 글에서는 벨만-포드 알고리즘의 개요, 원리, 문제 해결 과정 및 자바스크립트 구현 방법에 대해 다룰 것입니다.

1. 벨만-포드 알고리즘 개요

벨만-포드 알고리즘은 한 정점에서 다른 정점까지의 최단 경로를 찾기 위해 사용하는 그래프 알고리즘입니다. 이 알고리즘은 다음과 같은 특징을 가지고 있습니다:

  • 음의 가중치를 가진 간선이 있어도 사용할 수 있습니다.
  • 그래프에 사이클이 존재하지 않거나, 음의 사이클이 존재하지 않음을 확인할 수 있습니다.

2. 벨만-포드 알고리즘의 원리

벨만-포드 알고리즘은 다음과 같은 원리로 작동합니다:

  1. 시작 정점을 설정하고, 해당 정점의 거리 값을 0으로 초기화합니다. 다른 모든 정점의 거리 값은 무한대로 설정합니다.
  2. 모든 간선을 한 번씩 검사하여, 각 간선의 시작 정점에서 끝 정점으로 가는 최단 거리를 갱신합니다.
  3. 이 과정을 정점의 개수 – 1만큼 반복합니다. (그래프에서 최단 경로는 최대 V - 1번의 간선을 지나기 때문입니다.)
  4. 마지막으로 모든 간선을 다시 검사하여 거리 값이 업데이트되는 경우가 있으면, 음의 사이클이 존재한다고 판단합니다.

3. 문제 설명

다음과 같은 문제를 해결해 보겠습니다:

문제:
n개의 도시와 m개의 도로가 주어질 때, 각 도로는 출발 도시에서 도착 도시까지의 거리를 나타내는 가중치가 있습니다.
일반적으로, 출발 도시는 1번 도시이고, 도착 도시는 n번 도시입니다.
1번 도시에서 n번 도시까지의 최단 거리를 구하는 함수를 작성하세요. (음의 간선이 있을 수 있습니다.)

4. 문제 해결 과정

이 문제를 벨만-포드 알고리즘을 이용해 해결하는 과정을 상세히 설명하겠습니다:

4.1. 입력 데이터 구조 설계

문제를 해결하기 위한 입력 데이터를 정의해야 합니다. 우선, 도시의 개수와 도로의 개수, 각 도로 정보를 담는 구조를 설계해야 합니다. 아래와 같이 객체 배열을 사용하여 도로를 표현할 수 있습니다:


    // 도시 개수와 도로 개수
    const n = 5; // 도시의 개수
    const m = 8; // 도로의 개수

    // 도로 정보 (출발 도시, 도착 도시, 거리)
    const roads = [
        { from: 1, to: 2, weight: 4 },
        { from: 1, to: 3, weight: 2 },
        { from: 2, to: 3, weight: 5 },
        { from: 2, to: 4, weight: 10 },
        { from: 3, to: 2, weight: 1 },
        { from: 3, to: 4, weight: 3 },
        { from: 4, to: 5, weight: 3 },
        { from: 2, to: 5, weight: 12 }
    ];
    

4.2. 초기화

다음으로, 거리 값을 초기화합니다. 1번 도시의 거리는 0으로, 나머지 도시는 무한대로 설정합니다:


    const INF = Infinity; // 무한대 값
    const distance = Array(n + 1).fill(INF); // 1부터 n까지의 거리 배열
    distance[1] = 0; // 시작 도시의 거리 초기화
    

4.3. 벨만-포드 알고리즘 구현

이제 벨만-포드 알고리즘을 구현합니다. 간선을 n-1번 반복하여 거리 값을 갱신합니다:


    for (let i = 1; i < n; i++) {
        for (const road of roads) {
            if (distance[road.from] + road.weight < distance[road.to]) {
                distance[road.to] = distance[road.from] + road.weight;
            }
        }
    }
    

4.4. 음의 사이클 체크

마지막으로, 모든 간선을 다시 검사하여 음의 사이클이 있는지 확인하는 과정을 구현합니다:


    let hasNegativeCycle = false;

    for (const road of roads) {
        if (distance[road.from] + road.weight < distance[road.to]) {
            hasNegativeCycle = true; // 음의 사이클 존재
            break;
        }
    }

    if (hasNegativeCycle) {
        console.log("음의 사이클이 존재합니다.");
    } else {
        console.log("최단 거리:", distance[n]);
    }
    

5. 전체 코드

위의 모든 과정을 종합하여 완전한 자바스크립트 코드를 만들어 보겠습니다:


    function bellmanFord(n, roads) {
        const INF = Infinity;
        const distance = Array(n + 1).fill(INF);
        distance[1] = 0;

        // 벨만-포드 알고리즘
        for (let i = 1; i < n; i++) {
            for (const road of roads) {
                if (distance[road.from] + road.weight < distance[road.to]) {
                    distance[road.to] = distance[road.from] + road.weight;
                }
            }
        }

        // 음의 사이클 체크
        let hasNegativeCycle = false;
        for (const road of roads) {
            if (distance[road.from] + road.weight < distance[road.to]) {
                hasNegativeCycle = true;
                break;
            }
        }

        if (hasNegativeCycle) {
            console.log("음의 사이클이 존재합니다.");
        } else {
            console.log("최단 거리:", distance[n]);
        }
    }

    // 사용 예시
    const n = 5;
    const roads = [
        { from: 1, to: 2, weight: 4 },
        { from: 1, to: 3, weight: 2 },
        { from: 2, to: 3, weight: 5 },
        { from: 2, to: 4, weight: 10 },
        { from: 3, to: 2, weight: 1 },
        { from: 3, to: 4, weight: 3 },
        { from: 4, to: 5, weight: 3 },
        { from: 2, to: 5, weight: 12 }
    ];

    bellmanFord(n, roads);
    

6. 마무리

이번 글에서는 벨만-포드 알고리즘의 기본 개념과 원리, 문제를 해결하는 과정을 자세히 살펴보았습니다. 해당 알고리즘은 음의 가중치를 가진 간선이 있을 때 유용하며, 그래프 이론을 학습하는 데 중요한 알고리즘 중 하나입니다. 코딩테스트에서 자주 출제되는 주제니만큼, 반드시 숙지하시기 바랍니다.

앞으로도 다양한 알고리즘과 문제 풀이를 통해 실력을 쌓기를 바라며, 질문이 있으면 언제든지 댓글로 남겨주세요. 감사합니다!

자바스크립트 코딩테스트 강좌, 효율적으로 해킹하기

자바스크립트는 웹 개발에 있어 가장 중요한 언어 중 하나이며, 알고리즘 문제 풀이에서 그 중요성은 더욱 부각됩니다. 많은 기업들이 코딩 테스트를 통해 지원자의 문제 해결 능력과 코딩 능력을 평가합니다. 이번 글에서는 자바스크립트로 해결할 수 있는 알고리즘 문제를 통해 코딩 테스트의 전반적인 접근 방식과 문제 해결 과정을 자세히 설명하겠습니다.

문제 설명

문제 1: 두 수의 합

정수 배열 nums와 정수 target가 주어졌을 때, 배열에 있는 두 수의 합이 target과 일치하는 인덱스의 배열을 반환하는 함수를 작성하시오.

예를 들면:

  • nums = [2, 7, 11, 15], target = 9이라면, [0, 1]을 반환해야 합니다. (2 + 7 = 9)

문제 접근 방법

이 문제를 해결하기 위해 다음과 같은 접근 방식을 취할 수 있습니다.

  1. 이중 반복문 사용: 배열의 두 요소를 반복하여 합계를 계산하는 방법. 하지만 시간 복잡도가 O(n2)로 비효율적입니다.
  2. 해시맵 사용: 한 번의 반복으로 문제를 해결할 수 있습니다. 필요한 수를 해시맵에 저장하여, 현재 수와 target의 차이를 해시맵에서 찾아보는 방법입니다. 이 방법의 시간 복잡도는 O(n)입니다.

해결 방안: 해시맵을 이용한 코드

function twoSum(nums, target) {
    const map = new Map(); // 해시맵을 초기화

    for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i]; // 필요한 값을 계산

        if (map.has(complement)) {
            return [map.get(complement), i]; // 인덱스를 반환
        }
        
        map.set(nums[i], i); // 현재 수를 해시맵에 추가
    }
    
    return []; // 결과가 없으면 빈 배열 반환
}

코드 설명

위의 코드는 twoSum 함수를 정의합니다. 함수는 두 개의 매개변수, 즉 정수 배열 nums와 정수 target을 받습니다.

  1. 해시맵(map)을 초기화합니다.
  2. 주어진 배열 nums를 반복합니다.
  3. 각 수마다 complement을 계산합니다. (목표 값에서 현재 값을 뺀 결과)
  4. 해시맵에 complement이 있는지 확인합니다. 있으면 현재 인덱스와 저장된 인덱스를 반환합니다.
  5. 현재 수를 해시맵에 추가합니다.

복습

해시맵을 사용하여 문제를 해결한 방법은 효율적이었습니다. 그 이유는 코드가 O(n) 복잡도로 작동해 모든 입력 케이스에 대해 신속하게 반응할 수 있기 때문입니다. 코딩 테스트를 준비하면서 다양한 문제를 풀고 해결 방법을 이해하면 알고리즘과 자료구조에 대한 깊은 이해를 얻을 수 있습니다.

결론

자바스크립트 코딩 테스트에서의 성공은 문제를 읽고 이해하는 능력, 그리고 그에 적합한 알고리즘을 선택하는 능력에 달려 있습니다. 오늘 다룬 두 수의 합 문제는 그리 복잡하지 않지만, 알고리즘적 사고를 기르는 데 좋은 연습이 됩니다. 앞으로 더 많은 문제를 풀어보며 실력을 향상시켜 보세요!

자바스크립트 코딩테스트 강좌, 원하는 정수 찾기

자바스크립트 코딩테스트를 준비하며 가장 중요한 기술 중 하나는 주어진 문제를 정확하게 이해하고, 이를 효율적으로 해결하는 능력입니다. 이번 강좌에서는 ‘원하는 정수 찾기’라는 주제를 가지고 알고리즘 문제를 해결하는 과정을 자세히 살펴보겠습니다.

문제 설명

주어진 배열에서 특정 정수를 찾아 해당 정수의 인덱스를 반환하는 함수를 구현하세요. 만약 배열에 특정 정수가 없다면 -1을 반환해야 합니다.

함수 정의는 다음과 같습니다:

function findInteger(arr: number[], target: number): number

입력:

  • arr: 탐색할 정수 배열 (0 ≤ arr.length ≤ 10^5)
  • target: 찾고자 하는 정수 (-10^9 ≤ target ≤ 10^9)

출력:

  • target이 arr에 존재할 경우, target의 인덱스 값을 반환
  • target이 arr에 존재하지 않을 경우, -1을 반환

문제 분석

문제를 이해하기 위해서는 입력 배열에 대한 몇 가지 예시를 살펴보는 것이 좋습니다.

  • 예시 1: findInteger([1, 2, 3, 4, 5], 3)출력: 2 (3의 인덱스)
  • 예시 2: findInteger([10, 20, 30], 25)출력: -1 (25는 배열에 없음)
  • 예시 3: findInteger([1, 2, 3, 4, 5], 5)출력: 4 (5의 인덱스)

이 문제는 정수 배열에서 특정 정수를 찾는 것이기 때문에, 배열을 순회하면서 해당 정수를 찾는 방법이 가장 일반적일 것입니다. 그러나 최악의 경우, 배열의 길이가 100,000일 수 있으므로, 효율적인 해결책이 필요합니다.

해결 방안

이 문제를 해결하기 위해 두 가지 접근법을 고려해볼 수 있습니다:

  • 선형 탐색 (O(n))
  • 이진 탐색 (정렬된 배열일 경우, O(log n))

선형 탐색은 배열의 모든 요소를 순회하며 비교하는 방법입니다. 이 방법은 구현이 간단하지만, 최악의 경우 O(n) 시간이 소요됩니다. 하지만 이진 탐색은 주어진 배열이 정렬되어 있는 경우에만 가능한 방법입니다. 따라서 이 문제의 경우 배열이 정렬되어 있지 않을 가능성을 배제할 수 없습니다. 이에 따라 선형 탐색 방법을 선택해 보겠습니다.

구현

아래는 함수의 구체적인 구현 예제입니다:


function findInteger(arr, target) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
            return i; // 타겟을 찾았을 때 인덱스를 반환
        }
    }
    return -1; // 타겟이 없으면 -1 반환
}
            

위 코드는 다음과 같은 과정을 거칩니다:

  1. 주어진 배열 arr을 for 루프를 통해 순회합니다.
  2. 각 요소 arr[i]target과 비교합니다.
  3. 일치하는 경우 해당 인덱스 i를 반환합니다.
  4. 배열의 끝까지 도달했음에도 타겟을 찾지 못했을 경우, -1을 반환합니다.

이제 이 함수를 테스트해보겠습니다:


console.log(findInteger([1, 2, 3, 4, 5], 3)); // 2
console.log(findInteger([10, 20, 30], 25)); // -1
console.log(findInteger([1, 2, 3, 4, 5], 5)); // 4
            

시간 복잡도 분석

위의 알고리즘의 시간 복잡도는 O(n)입니다. 배열의 길이에 따라 탐색해야 하는 최대 횟수는 배열의 길이에 비례하기 때문입니다. 최악의 경우에는 배열의 모든 요소를 비교해야 할 수 있습니다.

공간 복잡도는 O(1)로, 추가적인 데이터 구조를 사용하지 않고 원래의 배열만을 활용하기 때문에 메모리 사용량이 일정합니다.

결론

이번 강좌에서는 자바스크립트를 이용해 ‘원하는 정수 찾기’ 문제를 해결하는 방법에 대해 살펴보았습니다. 문제를 분석하고, 적합한 알고리즘을 선택하여 구현하는 과정을 통해 코딩 테스트를 준비하는 데 중요한 기술을 연습할 수 있었습니다. 이와 같은 과정을 반복하면서 다양한 문제를 접하고 해결해 나간다면, 충분히 실력을 향상시킬 수 있을 것입니다. 앞으로도 많은 알고리즘 문제를 풀어보며 자신만의 해법을 찾아보시기 바랍니다.

자바스크립트 코딩테스트 강좌, 선분을 그룹으로 나누기

이 강좌는 자바스크립트 코딩 테스트에서 자주 출제되는 문제 중 하나인 “선분을 그룹으로 나누기”에 대해 다루고자 합니다.
본 문제는 주어진 선분들이 서로 겹치는 경우를 찾아서 이를 그룹으로 나누는 과정을 테스트합니다.
알고리즘 문제를 해결하는 과정에서 발생할 수 있는 다양한 상황과 고려해야 할 사항들을 상세히 살펴보겠습니다.

문제 정의

문제: 주어진 선분의 배열이 있을 때, 서로 겹치는 선분들을 그룹으로 묶어 그룹의 수를 반환하시오.

예를 들어, 다음과 같은 선분이 주어진다고 가정합시다:


선분: [[1, 3], [2, 4], [5, 6], [7, 10], [9, 11]]

이 배열에는 두 그룹이 존재합니다:

  • 첫 번째 그룹: [[1, 3], [2, 4]]
  • 두 번째 그룹: [[5, 6], [7, 10], [9, 11]]

문제 접근 방법

이 문제를 해결하기 위해 다음과 같은 접근 방식을 사용할 수 있습니다:

  1. 정렬: 선분의 시작점 또는 끝점을 기준으로 정렬합니다.
  2. 그룹화: 정렬된 선분을 순회하며 겹치는 선분들을 그룹으로 나눕니다.

1단계: 선분 정렬

선분을 시작점 기준으로 정렬합니다. 이렇게 하면 선분들이 겹치는 경우를 쉽게 판단할 수 있습니다.

2단계: 그룹화 로직 구현

정렬된 선분을 순회하면서 현재 선분이 이전 선분과 겹치는지를 확인합니다.
겹치지 않는 경우 새 그룹을 시작하고, 겹치는 경우 해당 그룹에 추가합니다.

예제 코드

위의 로직을 바탕으로 작성한 자바스크립트 코드는 다음과 같습니다.


function groupLines(lines) {
    // 1. 선분을 시작점 기준으로 정렬
    lines.sort((a, b) => a[0] - b[0]);

    let groups = [];
    let currentGroup = [];

    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];

        if (currentGroup.length === 0) {
            currentGroup.push(line);
        } else {
            // 현재 선분의 시작점이 이전 선분의 끝점보다 작거나 같으면 겹친다.
            if (line[0] <= currentGroup[currentGroup.length - 1][1]) {
                currentGroup.push(line);
            } else {
                // 겹치지 않으면 그룹을 저장하고 새 그룹 시작
                groups.push(currentGroup);
                currentGroup = [line];
            }
        }
    }

    // 마지막 그룹을 추가
    if (currentGroup.length > 0) {
        groups.push(currentGroup);
    }

    return groups.length;
}

// 예제 입력
const lines = [[1, 3], [2, 4], [5, 6], [7, 10], [9, 11]];
console.log(groupLines(lines));  // 출력: 2

코드 설명

위 코드에서는 다음과 같은 과정을 통해 선분을 그룹으로 나누었습니다:

  1. 정렬: 선분 배열을 시작점 기준으로 오름차순으로 정렬하였습니다.
  2. 그룹 탐색: 각 선분을 순회하면서 현재 선분이 이전 선분과 겹치는지를 확인하였습니다.
  3. 그룹 저장: 겹치지 않는 선분을 만나면 현재 그룹을 저장하고 새 그룹을 시작합니다.

복잡도 분석

이 알고리즘의 시간 복잡도는 주로 정렬을 수행하는 부분에서 결정됩니다. 정렬은 O(n log n)이고, 선분을 순회하면서 그룹을 나누는 과정은 O(n)입니다.
따라서 전체 시간 복잡도는 O(n log n)입니다.

공간 복잡도는 최악의 경우 모든 선분이 겹치지 않을 때 O(n)입니다.

정리

이번 강좌에서는 “선분을 그룹으로 나누기”라는 문제를 통해 선분이 겹치는 경우를 판단하고 그룹화하는 방법을 알아보았습니다.
정렬과 탐색이라는 기본적인 알고리즘 기법을 이용하여 문제를 효과적으로 해결하는 과정을 살펴보았습니다.

이러한 알고리즘 문제는 실제 코딩 테스트에서 자주 출제되므로, 위와 같은 접근 방식을 연습하고, 다양한 변형 문제를 풀어보는 것이 중요합니다.
다음 강좌에서도 유용한 코딩 테스트 문제를 다룰 예정이니 많은 기대 바랍니다!