1"""
2ํฌ ํฌ์ธํฐ (Two Pointer) ๊ธฐ๋ฒ
3Two Pointer Technique
4
5๋ ๊ฐ์ ํฌ์ธํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฐ์ด/๋ฆฌ์คํธ ๋ฌธ์ ๋ฅผ ํจ์จ์ ์ผ๋ก ํด๊ฒฐํฉ๋๋ค.
6"""
7
8from typing import List, Tuple, Optional
9
10
11# =============================================================================
12# 1. ๋ ์์ ํฉ (์ ๋ ฌ๋ ๋ฐฐ์ด)
13# =============================================================================
14def two_sum_sorted(arr: List[int], target: int) -> Optional[Tuple[int, int]]:
15 """
16 ์ ๋ ฌ๋ ๋ฐฐ์ด์์ ํฉ์ด target์ธ ๋ ์์ ์ธ๋ฑ์ค ์ฐพ๊ธฐ
17 ์๊ฐ๋ณต์ก๋: O(n), ๊ณต๊ฐ๋ณต์ก๋: O(1)
18 """
19 left, right = 0, len(arr) - 1
20
21 while left < right:
22 current_sum = arr[left] + arr[right]
23
24 if current_sum == target:
25 return (left, right)
26 elif current_sum < target:
27 left += 1 # ํฉ์ด ์์ผ๋ฉด ์ผ์ชฝ ํฌ์ธํฐ๋ฅผ ์ค๋ฅธ์ชฝ์ผ๋ก
28 else:
29 right -= 1 # ํฉ์ด ํฌ๋ฉด ์ค๋ฅธ์ชฝ ํฌ์ธํฐ๋ฅผ ์ผ์ชฝ์ผ๋ก
30
31 return None
32
33
34# =============================================================================
35# 2. ์ธ ์์ ํฉ (3Sum)
36# =============================================================================
37def three_sum(arr: List[int]) -> List[List[int]]:
38 """
39 ํฉ์ด 0์ธ ์ธ ์์ ์กฐํฉ ๋ชจ๋ ์ฐพ๊ธฐ (์ค๋ณต ์ ๊ฑฐ)
40 ์๊ฐ๋ณต์ก๋: O(nยฒ), ๊ณต๊ฐ๋ณต์ก๋: O(1)
41 """
42 arr.sort()
43 result = []
44 n = len(arr)
45
46 for i in range(n - 2):
47 # ์ค๋ณต ๊ฑด๋๋ฐ๊ธฐ
48 if i > 0 and arr[i] == arr[i - 1]:
49 continue
50
51 left, right = i + 1, n - 1
52
53 while left < right:
54 total = arr[i] + arr[left] + arr[right]
55
56 if total == 0:
57 result.append([arr[i], arr[left], arr[right]])
58 # ์ค๋ณต ๊ฑด๋๋ฐ๊ธฐ
59 while left < right and arr[left] == arr[left + 1]:
60 left += 1
61 while left < right and arr[right] == arr[right - 1]:
62 right -= 1
63 left += 1
64 right -= 1
65 elif total < 0:
66 left += 1
67 else:
68 right -= 1
69
70 return result
71
72
73# =============================================================================
74# 3. ๋ฌผ ๋ด๊ธฐ (Container With Most Water)
75# =============================================================================
76def max_water(heights: List[int]) -> int:
77 """
78 ๋ ๋ฒฝ ์ฌ์ด์ ๋ด์ ์ ์๋ ์ต๋ ๋ฌผ์ ์
79 ์๊ฐ๋ณต์ก๋: O(n), ๊ณต๊ฐ๋ณต์ก๋: O(1)
80 """
81 left, right = 0, len(heights) - 1
82 max_area = 0
83
84 while left < right:
85 # ํ์ฌ ๋ฉด์ ๊ณ์ฐ
86 width = right - left
87 height = min(heights[left], heights[right])
88 area = width * height
89 max_area = max(max_area, area)
90
91 # ๋ ๋ฎ์ ์ชฝ์ ํฌ์ธํฐ ์ด๋
92 if heights[left] < heights[right]:
93 left += 1
94 else:
95 right -= 1
96
97 return max_area
98
99
100# =============================================================================
101# 4. ์ ๋ ฌ๋ ๋ ๋ฐฐ์ด ํฉ๋ณ
102# =============================================================================
103def merge_sorted_arrays(arr1: List[int], arr2: List[int]) -> List[int]:
104 """
105 ์ ๋ ฌ๋ ๋ ๋ฐฐ์ด์ ํ๋์ ์ ๋ ฌ๋ ๋ฐฐ์ด๋ก ํฉ๋ณ
106 ์๊ฐ๋ณต์ก๋: O(n + m), ๊ณต๊ฐ๋ณต์ก๋: O(n + m)
107 """
108 result = []
109 i, j = 0, 0
110
111 while i < len(arr1) and j < len(arr2):
112 if arr1[i] <= arr2[j]:
113 result.append(arr1[i])
114 i += 1
115 else:
116 result.append(arr2[j])
117 j += 1
118
119 # ๋จ์ ์์ ์ถ๊ฐ
120 result.extend(arr1[i:])
121 result.extend(arr2[j:])
122
123 return result
124
125
126# =============================================================================
127# 5. ์ค๋ณต ์ ๊ฑฐ (์ ๋ ฌ๋ ๋ฐฐ์ด)
128# =============================================================================
129def remove_duplicates(arr: List[int]) -> int:
130 """
131 ์ ๋ ฌ๋ ๋ฐฐ์ด์์ ์ค๋ณต ์ ๊ฑฐ (in-place)
132 ๋ฐํ๊ฐ: ๊ณ ์ ํ ์์์ ๊ฐ์
133 ์๊ฐ๋ณต์ก๋: O(n), ๊ณต๊ฐ๋ณต์ก๋: O(1)
134 """
135 if not arr:
136 return 0
137
138 write_idx = 1 # ๋ค์ ๊ณ ์ ๊ฐ์ ์ธ ์์น
139
140 for read_idx in range(1, len(arr)):
141 if arr[read_idx] != arr[write_idx - 1]:
142 arr[write_idx] = arr[read_idx]
143 write_idx += 1
144
145 return write_idx
146
147
148# =============================================================================
149# 6. ํ๋ฌธ ๊ฒ์ฌ (Palindrome)
150# =============================================================================
151def is_palindrome(s: str) -> bool:
152 """
153 ๋ฌธ์์ด์ด ํ๋ฌธ์ธ์ง ๊ฒ์ฌ (์ํ๋ฒณ/์ซ์๋ง ๋น๊ต)
154 ์๊ฐ๋ณต์ก๋: O(n), ๊ณต๊ฐ๋ณต์ก๋: O(1)
155 """
156 left, right = 0, len(s) - 1
157
158 while left < right:
159 # ์ํ๋ฒณ/์ซ์๊ฐ ์๋๋ฉด ๊ฑด๋๋ฐ๊ธฐ
160 while left < right and not s[left].isalnum():
161 left += 1
162 while left < right and not s[right].isalnum():
163 right -= 1
164
165 if s[left].lower() != s[right].lower():
166 return False
167
168 left += 1
169 right -= 1
170
171 return True
172
173
174# =============================================================================
175# 7. ์ฌ๋ผ์ด๋ฉ ์๋์ฐ (์ต๋ ํฉ)
176# =============================================================================
177def max_sum_subarray(arr: List[int], k: int) -> int:
178 """
179 ํฌ๊ธฐ k์ธ ์ฐ์ ๋ถ๋ถ ๋ฐฐ์ด์ ์ต๋ ํฉ
180 ์๊ฐ๋ณต์ก๋: O(n), ๊ณต๊ฐ๋ณต์ก๋: O(1)
181 """
182 if len(arr) < k:
183 return 0
184
185 # ์ด๊ธฐ ์๋์ฐ ํฉ
186 window_sum = sum(arr[:k])
187 max_sum = window_sum
188
189 # ์๋์ฐ ์ฌ๋ผ์ด๋ฉ
190 for i in range(k, len(arr)):
191 window_sum = window_sum - arr[i - k] + arr[i]
192 max_sum = max(max_sum, window_sum)
193
194 return max_sum
195
196
197# =============================================================================
198# ํ
์คํธ
199# =============================================================================
200def main():
201 print("=" * 60)
202 print("ํฌ ํฌ์ธํฐ (Two Pointer) ๊ธฐ๋ฒ ์์ ")
203 print("=" * 60)
204
205 # 1. ๋ ์์ ํฉ
206 print("\n[1] ๋ ์์ ํฉ (์ ๋ ฌ๋ ๋ฐฐ์ด)")
207 arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
208 target = 10
209 result = two_sum_sorted(arr, target)
210 print(f" ๋ฐฐ์ด: {arr}")
211 print(f" ํ๊ฒ: {target}")
212 print(f" ๊ฒฐ๊ณผ: ์ธ๋ฑ์ค {result} -> {arr[result[0]]} + {arr[result[1]]} = {target}")
213
214 # 2. ์ธ ์์ ํฉ
215 print("\n[2] ์ธ ์์ ํฉ (3Sum)")
216 arr = [-1, 0, 1, 2, -1, -4]
217 result = three_sum(arr)
218 print(f" ๋ฐฐ์ด: {arr}")
219 print(f" ํฉ์ด 0์ธ ์กฐํฉ: {result}")
220
221 # 3. ๋ฌผ ๋ด๊ธฐ
222 print("\n[3] ๋ฌผ ๋ด๊ธฐ (Container With Most Water)")
223 heights = [1, 8, 6, 2, 5, 4, 8, 3, 7]
224 result = max_water(heights)
225 print(f" ๋์ด: {heights}")
226 print(f" ์ต๋ ๋ฌผ์ ์: {result}")
227
228 # 4. ์ ๋ ฌ๋ ๋ฐฐ์ด ํฉ๋ณ
229 print("\n[4] ์ ๋ ฌ๋ ๋ ๋ฐฐ์ด ํฉ๋ณ")
230 arr1 = [1, 3, 5, 7]
231 arr2 = [2, 4, 6, 8, 10]
232 result = merge_sorted_arrays(arr1, arr2)
233 print(f" ๋ฐฐ์ด1: {arr1}")
234 print(f" ๋ฐฐ์ด2: {arr2}")
235 print(f" ํฉ๋ณ ๊ฒฐ๊ณผ: {result}")
236
237 # 5. ์ค๋ณต ์ ๊ฑฐ
238 print("\n[5] ์ค๋ณต ์ ๊ฑฐ (in-place)")
239 arr = [1, 1, 2, 2, 2, 3, 4, 4, 5]
240 count = remove_duplicates(arr)
241 print(f" ์๋ณธ: [1, 1, 2, 2, 2, 3, 4, 4, 5]")
242 print(f" ๊ณ ์ ์์ ๊ฐ์: {count}")
243 print(f" ๊ฒฐ๊ณผ ๋ฐฐ์ด (์ {count}๊ฐ): {arr[:count]}")
244
245 # 6. ํ๋ฌธ ๊ฒ์ฌ
246 print("\n[6] ํ๋ฌธ ๊ฒ์ฌ")
247 test_strings = ["A man, a plan, a canal: Panama", "race a car", "Was it a car or a cat I saw?"]
248 for s in test_strings:
249 result = is_palindrome(s)
250 print(f" '{s}' -> {result}")
251
252 # 7. ์ฌ๋ผ์ด๋ฉ ์๋์ฐ
253 print("\n[7] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ (ํฌ๊ธฐ k ์ต๋ ํฉ)")
254 arr = [2, 1, 5, 1, 3, 2]
255 k = 3
256 result = max_sum_subarray(arr, k)
257 print(f" ๋ฐฐ์ด: {arr}, k={k}")
258 print(f" ์ต๋ ํฉ: {result}")
259
260 print("\n" + "=" * 60)
261
262
263if __name__ == "__main__":
264 main()