Skip to content

Commit cfbecb7

Browse files
committed
feat: product-of-array-except-self 풀이 추가 (공간복잡도 최적화)
1 parent aadc575 commit cfbecb7

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
class Solution:
2+
def productExceptSelf(self, nums: list[int]) -> list[int]:
3+
n = len(nums)
4+
answer = [1] * n
5+
6+
left = 1
7+
for i in range(n):
8+
answer[i] = left
9+
left *= nums[i]
10+
11+
right = 1
12+
for i in range(n-1, -1, -1):
13+
answer[i] *= right
14+
right *= nums[i]
15+
16+
return answer
17+
18+
"""
19+
================================================================================
20+
풀이 과정 - 09:43 시작 ~ 풀이 과정 떠올리지 못함
21+
================================================================================
22+
23+
1. 정수 배열 nums가 주어짐
24+
2. 배열 answer를 반환해야 하는데
25+
3. answer[i]는 nums의 모든 요소의 곱과 같다 (nums[i]를 제외한)
26+
4. O(n) 시간에 실행되는 알고리즘을 작성해야하고, 나누기 연산을 사용해서는 안된다
27+
5. 나누기 연산을 어떻게 이용 안하고 풀지?
28+
29+
────────────────────────────────────────────────────────────────────────────────
30+
6. Claude 도움 → answer[i] = (i 왼쪽의 곱) * (i 오른쪽의 곱)
31+
32+
33+
[1차 시도] 왼쪽/오른쪽 곱 배열 사용
34+
────────────────────────────────────────────────────────────────────────────────
35+
7. 각 위치의 왼쪽 누적곱과 오른쪽 누적곱을 미리 계산
36+
8. left[i] = nums[0] * ... * nums[i-1]
37+
9. right[i] = nums[i+1] * ... * nums[n-1]
38+
10. answer[i] = left[i] * right[i]
39+
40+
n = len(nums)
41+
left = [1] * n
42+
for i in range(1, n):
43+
left[i] = left[i-1] * nums[i-1]
44+
45+
right = [1] * n
46+
for i in range(n-2, -1, -1):
47+
right[i] = right[i+1] * nums[i+1]
48+
49+
return [left[i] * right[i] for i in range(n)]
50+
51+
11. 정상적으로 통과되는 것 확인 완료
52+
53+
54+
[2차 개선] 공간 복잡도 최적화
55+
────────────────────────────────────────────────────────────────────────────────
56+
12. left, right 배열을 따로 만들면 O(n) 공간 복잡도 추가 사용
57+
13. answer 배열을 재활용하면 추가 공간 없이 해결 가능
58+
14. 첫 번째 순회: answer에 왼쪽 누적곱 저장
59+
15. 두 번째 순회: answer에 오른쪽 누적곱을 곱하면서 최종 결과 완성
60+
61+
n = len(nums)
62+
answer = [1] * n
63+
64+
left = 1
65+
for i in range(n):
66+
answer[i] = left
67+
left *= nums[i]
68+
69+
right = 1
70+
for i in range(n-1, -1, -1):
71+
answer[i] *= right
72+
right *= nums[i]
73+
74+
return answer
75+
76+
16. 추가 공간 복잡도 O(n) → O(1)로 개선 완료 (answer 배열 제외)
77+
17. 최종 통과 확인 완료
78+
79+
80+
[문제 복기] 패턴 발견 과정
81+
────────────────────────────────────────────────────────────────────────────────
82+
- 문제를 다시 복기해보면
83+
- 각 위치에서 자기 자신을 제외한 모든 원소의 곱을 구해야함
84+
- 제약을 무시하면 이중 반복문 Brute Force로 풀 수 있음
85+
- 제약을 무시하면 나눗셈을 이용해서도 풀 수 있음
86+
- 나눗셈도 금지되고 O(n) 시간에 풀어야한다면?
87+
- "제외한다"를 어떻게 표현해주면 좋을까? (이 부분이 핵심)
88+
89+
작은 예시로 패턴 찾기:
90+
nums = [2, 3, 4]
91+
92+
answer[0] = 3 * 4 = 12
93+
answer[1] = 2 * 4 = 8
94+
answer[2] = 2 * 3 = 6
95+
96+
answer[0] = (없음) * (3 * 4) = 12
97+
answer[1] = (2) * (4) = 8
98+
answer[2] = (2 * 3) * (없음) = 6
99+
100+
- 패턴 발견 → 왼쪽 부분 * 오른쪽 부분
101+
- 해법 도출 → 왼쪽 부분의 모든 곱 * 오른쪽 부분의 모든 곱
102+
103+
패턴을 일반화하는 과정이 필요:
104+
answer[i] = nums[0] * ... * nums[i-1] * nums[i+1] * ... * nums[n-1]
105+
left[i] = nums[0] * ... * nums[i-1]
106+
right[i] = nums[i+1] * ... * nums[n-1]
107+
answer[i] = left[i] * right[i]
108+
109+
점화식 찾는데, DP적 사고 필요:
110+
왼쪽 누적곱:
111+
left[1] = nums[0]
112+
left[2] = nums[0] * nums[1] = left[1] * nums[1]
113+
left[3] = nums[0] * nums[1] * nums[2] = left[2] * nums[2]
114+
→ left[i] = left[i-1] * nums[i-1]
115+
116+
오른쪽 누적곱:
117+
right[2] = nums[3]
118+
right[1] = nums[3] * nums[2] = right[2] * nums[2]
119+
right[0] = nums[3] * nums[2] * nums[1] = right[1] * nums[1]
120+
→ right[i] = right[i+1] * nums[i+1]
121+
122+
관련해서 코드로 구현하는 과정이 필요함
123+
"""

0 commit comments

Comments
 (0)