1+ import java .util .HashMap ;
2+ import java .util .Map ;
3+
4+ class Solution {
5+ /**
6+ * 풀이요약: 범위를 반씩 나누며 곱을 캐싱하고, 제외할 인덱스만 골라 탐색하는 분할 정복 기반 배타 곱 계산
7+ *
8+ *
9+ * 풀이결과:
10+ *
11+ * Runtime: 872 ms (Beats 3.62%)
12+ * Memory: 137.29 MB (Beats 5.61%)
13+ * Space Complexity: O(NlogN)
14+ * - 길이 N인 배열을 절반씩 자른 범위 안에 수들을 곱한 값을 저장 O(NlogN)
15+ * - 길이 N인 출력 배열 생성 O(N)
16+ * > O(N) + O(NlogN) > O(NlogN)
17+ *
18+ * Time Complexity: O(NlogN)
19+ * - 길이 N인 배열을 절반씩 잘라가면서 안에 수들을 곱하기 O(NlogN)
20+ * - 0~N 범위를 절반씩 잘라가면서 i가 포함되지 않은 범위 수를 곱하기 O(logN)
21+ * - N개의 숫자에 대해서 찾기 => O(NlogN)
22+ * > O(NlogN) + O(NlogN) > O(NlogN)
23+ */
24+ public int [] productExceptSelf1 (int [] nums ) {
25+ Map <String , Integer > mp = new HashMap <>();
26+ fndRngMul (mp , nums , 0 , nums .length );
27+ int [] ans = new int [nums .length ];
28+ for (int i = 0 ; i < nums .length ; i ++) {
29+ int val = binaryFnd (mp , i , 0 , nums .length );
30+ ans [i ] = val ;
31+ }
32+
33+ return ans ;
34+ }
35+
36+ /***
37+ * 특정 idx를 제외한 구간 곱을 재귀적으로 찾는다.
38+ *
39+ * - 범위가 단일 원소면 곱에 포함될 값이 없으므로 1 반환
40+ * - 범위를 반으로 자르고, idx가 속하지 않은 부분의 곱을 즉시 사용
41+ * - idx가 속한 쪽은 계속 재귀 탐색
42+ */
43+ private int binaryFnd (Map <String , Integer > mp , int idx , int str , int end ) {
44+ int val = 1 ;
45+
46+ if (end - str == 1 ) {
47+ return val ;
48+ }
49+
50+ int mid = (str + end ) / 2 ;
51+ String leftKey = str + "_" + mid ;
52+ String rightKey = mid + "_" + end ;
53+ // System.out.println("----idx->"+idx+" mid->"+mid);
54+ // idx는 좌측에 위치하기 때문에 우측 값을 곱함.
55+ if (idx < mid ) {
56+ // 하지만 다은 idx가 포함된 범위를 탐색
57+ int leftVal = binaryFnd (mp , idx , str , mid );
58+ // System.out.println(". ----leftVal->"+leftVal+" base->"+mp.get(rightKey)+"
59+ // val-> "+val);
60+ val = mp .get (rightKey ) * leftVal ;
61+ } else { // idx는 우측에 위치하기 때문에 좌측 값을 곱함.
62+ int rightVal = binaryFnd (mp , idx , mid , end );
63+ // System.out.println(". ----rightVal->"+rightVal+" base->"+mp.get(leftKey)+"
64+ // val-> "+val);
65+ val = mp .get (leftKey ) * rightVal ;
66+ }
67+ return val ;
68+ }
69+
70+ /**
71+ * 배열의 범위 [str, end) 내 값들의 곱을 구해 캐싱한다.
72+ *
73+ * - 원소가 하나만 있는 구간이면 nums[str] 저장
74+ * - 구간을 반으로 나누어 왼쪽/오른쪽 곱을 재귀적으로 계산
75+ * - 전체 구간이 아니라면 left * right 를 캐싱
76+ * - 전체 구간(0~N)은 제외 곱 계산 시 무시해야 하므로 '1' 저장
77+ */
78+ private int fndRngMul (Map <String , Integer > mp , int [] nums , int str , int end ) {
79+ String k = str + "_" + end ;
80+
81+ if (end - str == 1 ) {
82+ // System.out.println("put k->"+k+" v->"+v);
83+ mp .put (k , nums [str ]);
84+ return nums [str ];
85+ }
86+ int mid = (str + end ) / 2 ;
87+ int leftRngMul = fndRngMul (mp , nums , str , mid );
88+ int rightRngMul = fndRngMul (mp , nums , mid , end );
89+
90+ int v = 1 ;
91+ // 전체 곱은 구하지 않음.
92+ if (str == 0 && end == nums .length ) {
93+ v = 1 ;
94+ } else {
95+ v = leftRngMul * rightRngMul ;
96+ }
97+ mp .put (k , v );
98+ // System.out.println("put2 k->"+k+" v->"+v);
99+ return v ;
100+ }
101+ }
0 commit comments