자바스크립트 코딩테스트 강좌, 선물 전달하기

문제 설명

당신은 여러 친구들에게 선물을 전달하는 일을 맡았습니다. 각각의 친구에게 선물을 전달하기 위해서는 그 친구의 고유한 ID가 필요합니다. 모든 친구의 ID와 선물의 ID가 주어질 때, 선물을 전달할 수 있는지를 확인해야 합니다.

문제 정의

각 친구들은 다음과 같은 정보를 가지고 있다고 가정합니다:

  • 친구의 ID
  • 받고 싶은 선물의 ID
  • 선신호 (이 친구가 선물을 받을 수 있는지 여부)

입력으로 주어지는 배열에는 여러 친구들의 정보가 담겨 있습니다. 이 배열에서 각 친구의 ID와 받고 싶은 선물의 ID를 기준으로, 정확하게 선물을 전달할 수 있는지를 판단하는 함수를 작성하시오.

입력 형식


    [
        { friendId: 1, giftId: 101, signal: true },
        { friendId: 2, giftId: 102, signal: false },
        { friendId: 3, giftId: 101, signal: true }
    ]
    

출력 형식


    [
        { friendId: 1, giftId: 101, canReceive: true },
        { friendId: 2, giftId: 102, canReceive: false },
        { friendId: 3, giftId: 101, canReceive: true }
    ]
    

문제 해결 방법

이 문제를 해결하기 위해서는 각 친구의 정보가 담긴 배열을 순회하면서 선물 받을 수 있는지를 판단해야 합니다. 기본적으로 friend’s ID와 gift’s ID가 일치하면 선물을 받을 수 있다고 판단할 수 있습니다. 그러나 friend의 signal 값이 false인 경우 선물을 받을 수 없기 때문에 이를 고려해야 합니다.

알고리즘 설명


    function canGiftsBeReceived(friends) {
        return friends.map(friend => {
            const canReceive = (friend.signal === true && friend.friendId === friend.giftId);
            return { ...friend, canReceive: canReceive };
        });
    }
    

위의 코드는 주어진 친구들의 정보를 받아서 각 친구가 선물을 받을 수 있는지를 판단하여 새로운 배열을 반환합니다.

세부 단계

  1. 함수 정의: canGiftsBeReceived라는 함수를 정의하고 friends라는 매개변수를 받습니다. 이 매개변수는 친구들의 정보가 담긴 배열입니다.
  2. 배열 순회: map 메서드를 통해 주어진 친구 배열을 순회합니다. 각 친구에 대해 friend라는 지역 변수를 사용합니다.
  3. 조건 체크: 각 친구에 대해 signaltrue이고 friendIdgiftId와 일치하는지를 체크하여 canReceive 값에 결과를 저장합니다.
  4. 결과 객체 생성: 각 친구의 정보를 기반으로 새로운 객체를 생성합니다. 이 객체는 기존 친구 정보와 canReceive 값을 포함합니다.
  5. 결과 반환: 최종적으로 변환된 배열을 반환합니다.

예제 코드


    const friends = [
        { friendId: 1, giftId: 101, signal: true },
        { friendId: 2, giftId: 102, signal: false },
        { friendId: 3, giftId: 101, signal: true }
    ];

    const result = canGiftsBeReceived(friends);
    console.log(result);
    

결과


    [
        { friendId: 1, giftId: 101, signal: true, canReceive: true },
        { friendId: 2, giftId: 102, signal: false, canReceive: false },
        { friendId: 3, giftId: 101, signal: true, canReceive: true }
    ]
    

위의 결과는 각 친구가 선물을 받을 수 있는지를 명확히 보여줍니다. 이 방법을 통해 선물을 안전하게 전달할 수 있습니다.

마무리

이번 강좌에서는 기본적인 배열과 객체를 활용한 문제 해결 방법을 살펴보았습니다. 알고리즘 문제를 해결하기 위해서는 문제를 체계적으로 분석하고 적절한 알고리즘을 적용하는 것이 중요합니다. 여러분이 자바스크립트를 통해 다양한 문제를 해결해 나가는 데 큰 도움이 되길 바랍니다.

© 2023 알고리즘 문제풀이 강좌

자바스크립트 코딩테스트 강좌, 선택 정렬

1. 서론

코딩 테스트, 알고리즘 문제 풀이에 있어서 정렬 알고리즘은 필수적으로 익혀야 할 주제입니다. 다양한 배열의 정렬 방법 중에서 선택 정렬(Selection Sort)은 그 구현이 간단하고, 그 과정이 직관적이기 때문에 많은 사람들에게 초기 단계에서 배우기 좋은 알고리즘으로 여겨집니다. 이번 강좌에서는 선택 정렬의 개념과 원리, 그리고 자바스크립트로 구현하는 방법을 자세히 알아보겠습니다.

2. 선택 정렬(Selection Sort)란?

선택 정렬은 배열을 정렬하는 간단한 알고리즘으로, 주어진 배열을 반복하면서 가장 작은 값(또는 가장 큰 값을) 찾아서 현재 위치와 교환하는 방식으로 동작합니다. 이 알고리즘은 매 회전마다 정렬된 부분과 정렬되지 않은 부분으로 나누어 진행됩니다.

2.1. 동작 원리

선택 정렬은 다음의 방법으로 동작합니다:

  • 배열의 첫 번째 요소부터 시작하여, 배열의 나머지 요소 중에서 가장 작은 요소를 찾아 첫 번째 요소와 교환합니다.
  • 두 번째 요소부터 같은 과정을 반복하여, 두 번째 요소와 두 번째 이후의 요소 중에서 가장 작은 요소를 교환합니다.
  • 이 과정을 배열의 마지막 요소까지 반복합니다.

2.2. 선택 정렬의 시간 복잡도

선택 정렬의 시간 복잡도는 최악의 경우와 평균 경우 모두 O(n^2)입니다. 이는 배열의 크기에 따라 성능이 급격히 저하될 수 있다는 것을 의미합니다. 따라서 선택 정렬은 소규모 데이터 세트에서 사용할 때 가장 적합합니다.

3. 선택 정렬 구현하기

이번 장에서는 선택 정렬을 자바스크립트로 구현해보겠습니다.

3.1. 기본 구현

아래의 코드는 선택 정렬을 구현한 기본적인 자바스크립트 함수입니다:


function selectionSort(arr) {
    const n = arr.length;

    for (let i = 0; i < n - 1; i++) {
        // 현재 인덱스(i)를 후보로 초기화
        let minIndex = i;

        // 나머지 배열을 스캔하여 가장 작은 요소의 인덱스를 찾음
        for (let j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j; // 더 작은 값을 찾으면, minIndex를 업데이트
            }
        }

        // 후보가 된 최소값과 현재 위치(i)를 교환
        // 현재 위치가 최소값이 아니라면 교환
        if (minIndex !== i) {
            [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
        }
    }

    return arr;
}

// 사용 예
const unsortedArray = [64, 25, 12, 22, 11];
const sortedArray = selectionSort(unsortedArray);
console.log(sortedArray); // [11, 12, 22, 25, 64]
    

3.2. 코드 설명

위의 코드는 선택 정렬 알고리즘을 사용하는 함수입니다. 함수를 하나씩 분석해 보겠습니다:

  1. const n = arr.length;: 배열의 길이를 구합니다.
  2. for (let i = 0; i < n - 1; i++): 첫 번째 루프는 배열의 각 요소를 순회합니다.
  3. let minIndex = i;: 현재 가장 작은 값의 인덱스를 초기화합니다.
  4. for (let j = i + 1; j < n; j++): 두 번째 루프는 나머지 배열을 순회하면서 가장 작은 요소의 인덱스를 찾습니다.
  5. if (arr[j] < arr[minIndex]) { minIndex = j; }: 배열의 현재 요소가 현재의 최소값보다 작으면, 최소값의 인덱스를 업데이트합니다.
  6. if (minIndex !== i) { [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]; }: 최종적으로 최소값이 현재 인덱스 위치에 없으면, 교환을 수행합니다.

4. 최적화된 선택 정렬

기본 선택 정렬은 최적화를 추가할 수 있습니다. 불필요한 교환을 줄이는 방법을 추가하여 성능을 조금 더 향상시킬 수 있습니다. 예를 들어, 정렬이 완료된 상태를 확인하여 더 이상의 교환이 필요 없을 경우 루프를 종료하고 성능을 높이는 방법이 있습니다. 아래 코드는 최적화된 선택 정렬을 보여줍니다:


function optimizedSelectionSort(arr) {
    const n = arr.length;
    let isSorted = true;

    for (let i = 0; i < n - 1; i++) {
        let minIndex = i;

        for (let j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
                isSorted = false; // 교환이 발생할 것임을 메모
            }
        }

        if (minIndex !== i) {
            [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
        }

        // 배열이 이미 정렬된 경우 루프 종료
        if (isSorted) break;
    }

    return arr;
}

// 사용 예
const unsortedArray = [64, 25, 12, 22, 11];
const sortedArray = optimizedSelectionSort(unsortedArray);
console.log(sortedArray); // [11, 12, 22, 25, 64]
    

4.1. 최적화 설명

최적화된 선택 정렬 함수는 초기화 단계에서 let isSorted = true;를 사용하여 배열이 정렬되었음을 추적합니다. 각 반복 후, 배열에 실제 교환이 있었다면 해당 플래그를 false로 설정합니다. 만약 현재 반복에서 교환이 발생하지 않았다면 배열이 완전히 정렬되었으며, 반복문을 종료합니다.

5. 실용적인 예제

선택 정렬을 활용하여 실제로 데이터를 정렬하는 예제를 보여드리겠습니다. 예를 들어, 학생의 성적 데이터를 정렬할 수 있습니다. 이를 통해 학생들의 성적을 비교하거나 필요한 정보를 제공하는 데 도움을 줄 수 있습니다.


const students = [
    { name: "Emily", score: 85 },
    { name: "David", score: 92 },
    { name: "Sophie", score: 76 },
    { name: "John", score: 89 },
    { name: "Max", score: 90 },
];

function selectionSortByScore(arr) {
    const n = arr.length;
    for (let i = 0; i < n - 1; i++) {
        let minIndex = i;
        for (let j = i + 1; j < n; j++) {
            if (arr[j].score < arr[minIndex].score) {
                minIndex = j;
            }
        }
        if (minIndex !== i) {
            [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
        }
    }
    return arr;
}

const sortedStudents = selectionSortByScore(students);
console.log(sortedStudents);
    

5.1. 실용적 예제 설명

위 코드는 학생의 성적을 기준으로 배열을 정렬하는 방법을 보여줍니다. 각 학생은 이름과 점수 객체로 표현되며, selectionSortByScore 함수를 통해 성적 오름차순으로 정렬되어 결과를 제공합니다.

6. 결론

선택 정렬은 구현이 간단하여 초보자들이 알고리즘의 기본 원리를 이해하는 데 매우 유용한 방법입니다. 그러나 선택 정렬은 O(n²)의 시간 복잡도를 가지고 있기 때문에 대규모 데이터의 경우 효율이 떨어지며, 실제 프로덕션 환경에서는 퀵 정렬, 병합 정렬과 같은 더 나은 알고리즘을 사용하는 것이 좋습니다. 하지만, 선택 정렬을 통해 알고리즘의 기초를 다지는 것은 중요한 학습 과정입니다. 앞으로 코딩 테스트 준비에 이 지식이 많은 도움이 되기를 바랍니다.

자바스크립트 코딩테스트 강좌, 물의 양 구하기

이번 강좌에서는 자바스크립트를 활용하여 코딩테스트에 자주 출제되는 문제인 ‘물의 양 구하기’ 문제를 다루어 보겠습니다. 이 문제는 다양한 알고리즘 설계 패턴을 익히고, 배열을 효과적으로 활용할 수 있는 좋은 기회가 될 것입니다.

문제 정의

주어진 높이의 막대기 배열이 있을 때, 비가 내렸을 때 모일 수 있는 물의 양을 구하는 문제입니다. 막대기의 높이는 배열의 각 요소로 주어집니다.

예를 들어, 배열이 [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]이라면, 모일 수 있는 물의 양은 6입니다. 이 예제에서 각 막대기 사이에 물이 어떻게 저장되는지를 시각적으로 나타내면 다음과 같습니다:

물 저장 시각화

문제를 해결하기 위한 접근법

이 문제를 해결하기 위해 사용할 수 있는 다양한 방법이 있습니다. 가장 흔히 사용되는 두 가지 접근 방법은 다음과 같습니다.

  1. 투 포인터(Two Pointers) 방법

    이 방법은 두 개의 포인터를 양쪽에서 중앙으로 이동시키면서 진행합니다. 각 포인터는 왼쪽과 오른쪽에서 높이를 추적하며, 이를 통해 물이 고일 수 있는 위치를 계산합니다.

  2. 동적 프로그래밍(Dynamic Programming)

    이 방법은 각 위치에서 왼쪽과 오른쪽에서 가장 높은 막대기를 찾고, 그 중 더 낮은 막대기로부터 물의 양을 계산합니다. 하지만 이 방법은 추가적인 메모리를 많이 사용한다는 단점이 있습니다.

JavaScript로 문제 해결하기

먼저 투 포인터 방법을 사용하여 코드를 작성해 보겠습니다. 우리는 배열의 양 끝에서 시작하여 중앙으로 이동하며 물의 양을 계산하는 알고리즘을 구현합니다.

코드 예제


function trap(height) {
    if (height.length === 0) return 0;

    let left = 0;
    let right = height.length - 1;
    let leftMax = 0;
    let rightMax = 0;
    let waterTrapped = 0;

    while (left < right) {
        if (height[left] < height[right]) {
            if (height[left] >= leftMax) {
                leftMax = height[left];
            } else {
                waterTrapped += leftMax - height[left];
            }
            left++;
        } else {
            if (height[right] >= rightMax) {
                rightMax = height[right];
            } else {
                waterTrapped += rightMax - height[right];
            }
            right--;
        }
    }

    return waterTrapped;
}

            

위의 함수는 height 배열을 입력받아 비가 내린 후 모일 수 있는 물의 양을 계산합니다.

코드 설명

위 코드를 단계별로 살펴보겠습니다:

  1. 초기화: left는 0으로 초기화하고, right는 배열의 마지막 인덱스(길이 – 1)로 초기화합니다. leftMaxrightMax는 각각의 방향에서의 최대 높이를 저장합니다. waterTrapped는 최종적으로 저장되는 물의 양을 나타냅니다.
  2. 루프 실행: leftright보다 작을 때까지 루프를 실행합니다. 각 반복에서, 왼쪽과 오른쪽의 높이를 비교하여 더 낮은 쪽에서 물의 양을 계산합니다.
  3. 물의 양 계산: 왼쪽의 높이가 더 낮은 경우, 왼쪽의 최대 높이와 현재 높이를 비교하여 물이 저장될 수 있는 양을 계산합니다. 마찬가지로 오른쪽에서 같은 작업을 수행합니다.
  4. 결과 반환: 모든 루프가 완료된 후, waterTrapped 변수를 반환하여 최종 결과를 출력합니다.

코드 테스트

이제 우리가 작성한 알고리즘을 테스트해 보겠습니다. 아래와 같은 여러 가지 예제를 통해 함수의 성능을 확인할 수 있습니다:


console.log(trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])); // 6
console.log(trap([4, 2, 0, 3, 2, 5])); // 9
console.log(trap([])); // 0
console.log(trap([1, 0, 1])); // 1

            

각 출력값은 예측된 물의 양과 일치해야 합니다. 다음과 같은 결과가 출력됩니다:

  • trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) – 6
  • trap([4, 2, 0, 3, 2, 5]) – 9
  • trap([]) – 0
  • trap([1, 0, 1]) – 1

성능 분석

위의 알고리즘은 O(n)의 시간 복잡도를 가지며, O(1)의 공간 복잡도를 갖습니다. 이는 입력 배열의 크기에 비례하여 수행 시간과 공간을 필요로 함을 의미합니다. 따라서 대량의 데이터에 대해서도 효율적으로 동작합니다.

만약 동적 프로그래밍을 사용한다면 높이를 저장하기 위한 추가 배열을 필요로 하므로 공간 복잡도는 O(n)이 됩니다. 그러나 시간 복잡도는 동일하게 O(n)입니다.

결론

이번 강좌에서는 ‘물의 양 구하기’ 문제에 대해 논의하고, 자바스크립트를 사용하여 이를 해결하는 방법을 소개했습니다. 투 포인터 알고리즘을 사용하여 문제를 효율적으로 해결할 수 있었고, 코드의 구현 및 성능 분석 또한 진행했습니다.

이 문제는 다른 알고리즘 문제에 적용할 수 있는 중요한 개념들이 포함되어 있으므로, 다른 유사한 문제도 연습해 보신다면 알고리즘 실력을 더욱 향상시킬 수 있을 것입니다.

이상으로 자바스크립트 코딩테스트 강좌를 마치겠습니다. 여러분의 코딩 실력 향상에 도움이 되기를 바랍니다!

자바스크립트 코딩테스트 강좌, DDR을 해보자

많은 기업들이 코딩 테스트를 통해 지원자의 알고리즘과 문제 해결 능력을 평가합니다. 이번 글에서는 자바스크립트를 활용하여 DDR(Dance Dance Revolution) 게임을 구현하는 알고리즘 문제를 풀어보겠습니다.

이 글에서는 문제의 이해, 해결 방법, 코드 작성 및 테스트 과정을 자세히 설명할 것입니다.

문제 설명

다음은 DDR 게임의 간단한 버전입니다. 게임은 아래와 같은 형식으로 진행됩니다.

사용자는 아래와 같은 4개의 화살표를 보고 해당 키를 눌러야 합니다:

  • ↑ (Up)
  • ↓ (Down)
  • ← (Left)
  • → (Right)

게임의 목표는 주어진 화살표 입력 순서에 따라 정확하게 키를 눌러 점수를 얻는 것입니다. 만약 잘못된 화살표를 누르면 점수를 잃게 됩니다.

당신은 n개의 화살표가 주어졌을 때, 사용자 입력을 받아 점수를 계산하는 함수를 작성해야 합니다. 이 함수는 사용자가 입력한 화살표가 정답 순서와 일치하는 경우 점수를 1점 추가하고, 틀린 경우 1점을 감점합니다.

입력 형식

        - 화살표 배열: ["↑", "↓", "←", "→"]
        - 사용자 입력 배열: ["↑", "↑", "←", "→", "↓"]
        

출력 형식

사용자의 최종 점수를 반환합니다.

문제 해결 과정

이 문제를 해결하기 위해 먼저 알고리즘을 설계해 보겠습니다. 문제를 해결하기 위한 단계는 다음과 같습니다.

  1. 화살표 배열과 사용자 입력 배열을 선언합니다.
  2. 점수를 유지하기 위한 변수를 초기화합니다.
  3. 사용자 입력을 순회하며 각 입력과 정답을 비교합니다.
  4. 정확한 입력일 경우 점수를 1점 올리고, 틀린 경우 1점을 감점합니다.
  5. 모든 입력을 확인한 후, 최종 점수를 반환합니다.

이제 알고리즘이 명확해졌으므로, 코드를 작성해 보겠습니다.

코드 구현


function calculateScore(correctArrows, userArrows) {
    let score = 0;

    for (let i = 0; i < userArrows.length; i++) {
        if (userArrows[i] === correctArrows[i]) {
            score += 1; // 정답
        } else {
            score -= 1; // 틀림
        }
    }

    return score; // 최종 점수 반환
}

// 예시 사용
const correctArrows = ["↑", "↓", "←", "→"];
const userArrows = ["↑", "↑", "←", "→", "↓"];

const finalScore = calculateScore(correctArrows, userArrows);
console.log("최종 점수는:", finalScore);
        

코드 설명

작성한 코드를 단계별로 설명하겠습니다.

1. 함수 정의

함수 calculateScore는 화살표 배열과 사용자 입력 배열을 매개변수로 받습니다. 점수를 계산하기 위해 score 변수를 0으로 초기화합니다.

2. 반복문을 통한 검사

for 루프를 사용하여 사용자 입력 배열을 순회합니다. 각 사용자의 입력이 정답 화살표와 일치하는지 확인합니다.

일치하는 경우에는 점수를 1점 추가하고, 일치하지 않는 경우에는 점수를 1점 감점합니다.

3. 최종 점수 반환

모든 사용자 입력을 검사한 후 score 값을 반환합니다. 이 값이 최종 점수입니다.

코드 테스트

코드가 올바르게 작동하는지 확인하기 위해 몇 가지 테스트 케이스를 만들어 보겠습니다.

테스트 케이스 1


const correct1 = ["↑", "↓", "←", "→"];
const user1 = ["↑", "↓", "←", "→"];

console.log("테스트 케이스 1 - 점수:", calculateScore(correct1, user1)); // 4
        

테스트 케이스 2


const correct2 = ["↑", "↓", "←", "→"];
const user2 = ["↑", "↑", "←", "→", "↓"];

console.log("테스트 케이스 2 - 점수:", calculateScore(correct2, user2)); // 2
        

테스트 케이스 3


const correct3 = ["↑", "↓", "←", "→"];
const user3 = ["→", "→", "→", "→"];

console.log("테스트 케이스 3 - 점수:", calculateScore(correct3, user3)); // -4
        

결론

이번 글에서는 자바스크립트를 사용하여 DDR 게임의 기본적인 알고리즘 문제를 해결해 보았습니다. 기초적인 문제 해결 방법과 코드를 작성하는 방법을 통해 자바스크립트의 기초를 더욱 확고히 할 수 있었습니다.

이 간단한 알고리즘 문제는 면접에서 자주 등장하는 유형의 문제 중 하나입니다. 따라서, 예제와 비슷한 문제를 많이 풀어보고, 자신만의 코드 스타일을 개발하는 것이 좋습니다. 감사합니다!

자바스크립트 코딩테스트 강좌, 구간 합 구하기 3

2023년 10월 5일

1. 문제 소개

구간 합 구하기 3 문제는 알고리즘 문제 해결 과정에서 자주 접하게 되는 문제 유형으로, 특히나 많은 데이터의 합을 계산할 때 효율성을 보여줍니다.
이 문제는 특정 구간의 합을 query를 통해 빠르게 구할 수 있는 방법에 대해 다룹니다.
구간 합을 알고리즘으로 계산하는 것은 특히 데이터베이스와 관련된 문제에서 매우 유용합니다.
오늘 우리는 이 문제를 해결하기 위해 여러 기법을 분석하고, 자바스크립트를 사용하여 해결해 보겠습니다.

2. 문제 설명

주어진 배열 A가 있으며, A의 길이는 N입니다. 양의 정수 쿼리 M이 주어질 때,
각 쿼리는 두 개의 정수 ij로 구성되어 있으며, 이 때 A[i] + A[i+1] + ... + A[j]의 값을
구해야 합니다. 쿼리는 최대 100,000개까지 존재할 수 있으며, A의 각 수는 최대 1,000,000입니다.
즉, 우리는 주어진 A 배열과 쿼리들을 기반으로 하여 각 구간의 합을 효율적으로 계산해야 합니다.

3. 문제 해결 전략

구간 합 문제를 해결하기 위해 우리는 두 가지 주요 방법을 사용할 것입니다.
– 첫째, 기본적인 방법인 이중 반복문을 사용하여 각 쿼리마다 합을 계산하는 방법입니다.
– 둘째, 구간 합을 미리 계산해 두고 쿼리 시 빠르게 결과를 도출하는 방법입니다.
특히, 두 번째 방법은 O(N)의 전처리를 통해 O(1) 시간으로 쿼리 결과를 얻을 수 있습니다.
따라서 우리가 이 문제를 더욱 효율적으로 해결하는 데 도움을 줄 것입니다.

4. 기본 방법 (O(N) x M)

이 방법은 매우 직관적이지만, 시간 복잡도는 O(N * M)입니다.
이 방식의 코드 구현은 다음과 같습니다.


function simpleRangeSum(A, queries) {
    const results = [];
    for (let [i, j] of queries) {
        let sum = 0;
        for (let k = i; k <= j; k++) {
            sum += A[k];
        }
        results.push(sum);
    }
    return results;
}
        

이 코드는 각 쿼리마다 해당 인덱스의 합을 반복하여 계산합니다. 그러나 문제의 제한 조건에서
이 방법은 비효율적입니다. 따라서 우리는 보다 효율적인 방법으로 넘어가야 합니다.

5. 효율적인 방법 (O(N) + O(1) per Query)

효율적인 방법을 살펴보면 먼저, 원본 배열의 구간 합을 저장할 배열을 만듭니다.
먼저, 구간 합 배열을 만드는 과정이 필요합니다. 구간 합 배열을 만들면,
각 쿼리의 결과는 단순히 두 쿼리 누적 합의 차로 얻을 수 있습니다.


function prefixSum(A) {
    const prefix = new Array(A.length + 1).fill(0);
    for (let i = 0; i < A.length; i++) {
        prefix[i + 1] = prefix[i] + A[i];
    }
    return prefix;
}

function rangeSum(A, queries) {
    const prefix = prefixSum(A);
    const results = [];
    for (let [i, j] of queries) {
        results.push(prefix[j + 1] - prefix[i]);
    }
    return results;
}
        

위의 구현에서, prefixSum 함수는 전체 데이터에 대한 누적 합을 계산하여 prefix 배열에 저장합니다.
이후에 각 쿼리는 O(1) 시간에 구간 합을 도출해낼 수 있습니다. 이 방식은
O(N) + O(1)로 쿼리를 처리할 수 있으므로 매우 효율적입니다.

6. 코드 설명

위의 코드를 분석하면 먼저 prefixSum 함수에서
배열의 길이에 하나를 더한 빈 배열을 만들고,
이 배열의 각 인덱스에 대한 누적 합을 계산합니다. 이 누적 합 배열을 통해
rangeSum 함수는 주어진 쿼리로부터 시작 인덱스 i와 종료 인덱스 j를 받아
빠르게 구간 합을 계산합니다.

이제 주어진 쿼리 수가 많을 때 시간 복잡도를 고려해야 하는데,
바로 위 솔루션이 시간 복잡도 면에서 매우 효율적이기 때문입니다.
각 쿼리를 처리하는 과정에서 불필요한 반복문이 키가 되었으며,
이 과정을 통해 결과를 도출함으로써 성능 개선이 이루어졌습니다.

7. 예제 테스트

다음과 같은 예제를 통해 위의 코드를 테스트해 보겠습니다.
배열 A = [1, 2, 3, 4, 5]이고, 쿼리는
[[0, 2], [1, 3], [2, 4]]입니다.


const A = [1, 2, 3, 4, 5];
const queries = [[0, 2], [1, 3], [2, 4]];
const results = rangeSum(A, queries);
console.log(results); // [6, 9, 12]
        

위의 테스트 결과는 각 쿼리에 대해 올바른 누적 합을 도출하고 있습니다. 테스팅 과정을 통해 우리의 코드가 정확하게 작동하는
지를 확인할 수 있으며, 이는 정답을 보장하는 중요한 과정입니다.

8. 마무리

구간 합 구하기 3는 다양한 알고리즘 문제 해결 스킬을 보여주는 훌륭한 예제입니다.
선수치기를 통해 구간 합 문제를 해결하는 것은 실제로도 일상적인 데이터 처리 작업에 많이 사용되고 있습니다.
오늘 배운 내용을 바탕으로 여러분도 비슷한 문제를 해결할 수 있는 능력을 기르셨길 바라며,
문제가 주어졌을 때 알고리즘을 어떻게 구성할지에 대한 통찰을 얻으셨기를 바랍니다.
자바스크립트를 통한 문제 해결 경험을 지속해서 쌓아가시기를 바랍니다.