Skip to content

Commit 493acb6

Browse files
committed
feat: 3sum 풀이 추가 (Two Pointer 최적화)
1 parent cfbecb7 commit 493acb6

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed

3sum/unpo88.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
class Solution:
2+
def threeSum(self, nums: list[int]) -> list[list[int]]:
3+
nums.sort()
4+
result = set()
5+
6+
for i in range(len(nums) - 2):
7+
# 양수인 경우 더 이상 탐색할 필요가 없음
8+
if nums[i] > 0:
9+
break
10+
11+
# i 중복 skip
12+
if i > 0 and nums[i] == nums[i - 1]:
13+
continue
14+
15+
left = i + 1
16+
right = len(nums) - 1
17+
18+
while left < right:
19+
total = nums[i] + nums[left] + nums[right]
20+
21+
if total == 0:
22+
result.add((nums[i], nums[left], nums[right]))
23+
24+
# 중복 건너뛰기
25+
while left < right and nums[left] == nums[left + 1]:
26+
left += 1
27+
# 중복 건너뛰기
28+
while left < right and nums[right] == nums[right - 1]:
29+
right -= 1
30+
left += 1
31+
right -= 1
32+
elif total < 0:
33+
left += 1
34+
else:
35+
right -= 1
36+
37+
return [list(t) for t in result]
38+
39+
40+
"""
41+
================================================================================
42+
풀이 과정 - 10:51 시작
43+
================================================================================
44+
45+
1. 정수 배열 nums가 주어지면
46+
2. 모든 세 쌍 [nums[i], nums[j], nums[k]]을 반환해야한다
47+
3. i != j, i != k, j != k 여야하고
48+
4. nums[i] + nums[j] + nums[k] == 0 이어야한다
49+
5. 중복된 세 쌍이 포함되지 않아야한다
50+
51+
52+
[1차 시도] Brute Force 3중 반복문
53+
────────────────────────────────────────────────────────────────────────────────
54+
6. 제약을 무시하고 생각해보면 Brute Force 3중 반복문으로 찾을 수 있을 것 같음
55+
7. 중복도 제거해야하니까 마지막에 set을 두고 처리
56+
57+
result = []
58+
n = len(nums)
59+
60+
for i in range(n):
61+
for j in range(i+1, n):
62+
for k in range(j+1, n):
63+
if nums[i] + nums[j] + nums[k] == 0:
64+
triplet = sorted([nums[i], nums[j], nums[k]])
65+
if triplet not in result:
66+
result.append(triplet)
67+
68+
return result
69+
70+
8. Time Limit Exceeded 발생
71+
9. O(n³) 시간 복잡도로 너무 느림
72+
73+
74+
[2차 시도] Two Sum 응용 (HashSet)
75+
────────────────────────────────────────────────────────────────────────────────
76+
10. 3개를 동시에 찾으려니까 어려운 것 같은데
77+
11. 2개를 먼저 구하고, 나머지 하나를 더하면 0이 되는지를 확인해볼까
78+
12. 두 수의 합 = 나머지 하나의 값
79+
13. -nums[i] = nums[j] + nums[k]
80+
14. 그럼 Two Sum 문제로 풀 수 있을듯?
81+
15. 두 개의 합이 set에 존재하는지를 체크하는 형태로 가보자
82+
83+
result = set()
84+
85+
for i in range(len(nums)):
86+
seen = set()
87+
for j in range(i + 1, len(nums)):
88+
complement = -(nums[i] + nums[j])
89+
if complement in seen:
90+
result.add(tuple(sorted([nums[i], nums[j], complement])))
91+
seen.add(nums[j])
92+
93+
return [list(x) for x in result]
94+
95+
16. 아 근데 이것도 Time Limit Exceeded가 발생하네
96+
17. O(n²) 시간 복잡도로 개선했지만 sorted() 호출과 set 연산이 추가 비용 발생
97+
18. 다른 접근 방법이 필요할 듯
98+
99+
100+
[3차 시도] Two Pointer 방식
101+
────────────────────────────────────────────────────────────────────────────────
102+
19. 그럼 Two Pointer 방식으로 풀어야할 것 같은데
103+
20. Two Pointer 방식을 사용하려면 정렬이 필요함
104+
21. Two Sum의 Two Pointer 패턴을 생각해보자
105+
22. 근데 3개를 동시에 찾으려면 어떻게 해야할까?
106+
23. i를 고정하고, 나머지에서 Two Sum으로 합이 -nums[i]인 두 수를 찾는다!
107+
108+
정렬 전: [-1, 0, 1, 2, -1, -4]
109+
정렬 후: [-4, -1, -1, 0, 1, 2]
110+
index: 0 1 2 3 4 5
111+
112+
i = 0, left = i + 1, right = len(nums) - 1
113+
[-4, -1, -1, 0, 1, 2]
114+
i L R
115+
116+
i = 1:
117+
[-4, -1, -1, 0, 1, 2]
118+
i L R
119+
120+
25. 정렬 먼저 한 후 합의 크기에 따라 포인터를 이동
121+
122+
nums.sort()
123+
result = set()
124+
125+
for i in range(len(nums) - 2):
126+
left = i + 1
127+
right = len(nums) - 1
128+
129+
while left < right:
130+
total = nums[i] + nums[left] + nums[right]
131+
132+
if total == 0:
133+
result.add((nums[i], nums[left], nums[right]))
134+
left += 1
135+
right -= 1
136+
elif total < 0:
137+
left += 1
138+
else:
139+
right -= 1
140+
141+
return [list(t) for t in result]
142+
143+
25. O(n²) 시간 복잡도 (정렬 O(n log n) + 반복문 O(n²))
144+
26. 정상적으로 통과되는 것 확인 완료
145+
27. 근데 이 방식으로 풀었는데 느리다
146+
147+
148+
[4차 개선] 중복 제거 최적화
149+
────────────────────────────────────────────────────────────────────────────────
150+
28. Claude에게 물어보니 tuple 생성할 때 상수 배수가 큰 것 같다고 함
151+
29. 중복을 미리 스킵해주는 방법을 알려줌
152+
30. i가 양수인 경우 더 이상 탐색할 필요 없음 (정렬되어 있으므로)
153+
31. i, left, right 중복 건너뛰기 추가
154+
155+
nums.sort()
156+
result = set()
157+
158+
for i in range(len(nums) - 2):
159+
# 양수인 경우 더 이상 탐색할 필요가 없음
160+
if nums[i] > 0:
161+
break
162+
163+
# i 중복 skip
164+
if i > 0 and nums[i] == nums[i - 1]:
165+
continue
166+
167+
left = i + 1
168+
right = len(nums) - 1
169+
170+
while left < right:
171+
total = nums[i] + nums[left] + nums[right]
172+
173+
if total == 0:
174+
result.add((nums[i], nums[left], nums[right]))
175+
176+
# 중복 건너뛰기
177+
while left < right and nums[left] == nums[left + 1]:
178+
left += 1
179+
while left < right and nums[right] == nums[right - 1]:
180+
right -= 1
181+
left += 1
182+
right -= 1
183+
elif total < 0:
184+
left += 1
185+
else:
186+
right -= 1
187+
188+
return [list(t) for t in result]
189+
190+
32. 중복 처리 최적화로 속도 개선
191+
33. 최종 통과 확인 완료
192+
"""

0 commit comments

Comments
 (0)