안녕하세요! 이번 포스팅에서는 구간 곱 구하기 문제를 해결하는 방법에 대해 알아보겠습니다. 이 문제는 주어진 배열에서 특정 구간의 곱을 빠르게 계산하는 알고리즘을 구현하는 것을 목표로 합니다. 이 강좌에서는 문제 정의부터 시작하여, 접근 방식, 구현, 그리고 성능 분석까지 단계적으로 진행됩니다.
문제 정의
주어진 정수 배열 nums
와 여러 쿼리 (i, j)
가 있을 때, 배열의 인덱스 i
부터 j
까지의 곱을 구해야 합니다. 이를 여러 번 반복해야 하므로, 시간 복잡도를 고려하여 효율적인 방법이 필요합니다.
예제
입력:
nums = [1, 2, 3, 4, 5]
쿼리 = [(0, 1), (1, 3), (0, 4)]
출력:
쿼리 0: 1 * 2 = 2
쿼리 1: 2 * 3 * 4 = 24
쿼리 2: 1 * 2 * 3 * 4 * 5 = 120
접근 방법
직관적으로는 각 쿼리에 대해 nums
배열에서 직접 곱을 계산할 수 있습니다. 하지만 이는 최악의 경우 O(n) 시간 복잡도를 요구하므로 여러 쿼리를 수행할 때 비효율적입니다. 따라서 아래와 같은 두 가지 접근 방법을 고려해볼 수 있습니다:
- 누적 곱 배열 (Prefix Product Array): 배열의 누적 곱을 미리 계산한 후, 각 쿼리에 대해 O(1)로 남은 곱을 빠르게 계산하는 방법입니다. 이 방법은 O(n)으로 누적 곱 배열을 준비한 후, 각 쿼리에서 O(1)에 계산하여 총 O(n + m) (여기서 m은 쿼리의 개수) 이 될 수 있습니다.
- 세그먼트 트리 (Segment Tree): 더 일반적인 방법으로, 세그먼트 트리를 사용하여 각 구간의 곱을 효율적으로 계산할 수 있습니다. 이 경우 초기화는 O(n log n)이고 쿼리는 O(log n)입니다.
이번 강좌에서는 첫 번째 방법인 누적 곱 배열을 사용한 구현을 진행하겠습니다.
구현
먼저, 누적 곱 배열을 생성한 다음, 각 쿼리마다 해당 구간의 곱을 계산해보겠습니다.
public class RangeProduct {
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5};
int[][] queries = {{0, 1}, {1, 3}, {0, 4}};
int[] result = rangeProduct(nums, queries);
for (int res : result) {
System.out.println(res);
}
}
public static int[] rangeProduct(int[] nums, int[][] queries) {
int n = nums.length;
int[] prefixProduct = new int[n + 1];
// 누적 곱 배열 생성
prefixProduct[0] = 1; // 초기값
for (int i = 1; i <= n; i++) {
prefixProduct[i] = prefixProduct[i - 1] * nums[i - 1];
}
int[] result = new int[queries.length];
// 쿼리 처리
for (int q = 0; q < queries.length; q++) {
int i = queries[q][0];
int j = queries[q][1];
// 구간곱 계산
result[q] = prefixProduct[j + 1] / prefixProduct[i]; // 0 ~ j의 곱 / 0 ~ (i-1)의 곱
}
return result;
}
}
성능 분석
위의 풀이는 효율적으로 쿼리를 처리할 수 있는 방법을 보여줍니다. 초기 누적 곱 배열을 만드는 데 O(n)시간이 걸리며, 각 쿼리 처리 시간은 O(1)입니다. 총 시간 복잡도는 O(n + m)이 됩니다. 이 방법은 querry 수가 많아질수록 효율적인 성능을 보여줍니다.
마무리
이번 강좌에서는 구간 곱 구하기 문제를 해결하는 방법을 살펴보았습니다. 누적 곱 배열을 사용하여 효율적으로 쿼리를 처리하는 방법을 배울 수 있었습니다. 세그먼트 트리나 펜윅 트리와 같은 더욱 복잡한 자료구조를 배우고 싶다면 다음 강좌를 기대해 주세요!
더 나아가기
이 강좌 이후에는 해당 문제를 다른 방식으로 해결하는 방법에 대해 다루어보는 것도 좋습니다. 다양한 방법론을 학습함으로써 알고리즘적 사고력을 확장할 수 있을 것입니다. 질문이나 토론이 필요한 부분이 있다면 댓글로 남겨주세요!