06_searching.py

Download
python 341 lines 9.6 KB
  1"""
  2์ด๋ถ„ ํƒ์ƒ‰ (Binary Search)
  3Binary Search Algorithms
  4
  5์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ์—์„œ O(log n) ์‹œ๊ฐ„์— ๊ฒ€์ƒ‰ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค.
  6"""
  7
  8from typing import List, Optional
  9import bisect
 10
 11
 12# =============================================================================
 13# 1. ๊ธฐ๋ณธ ์ด๋ถ„ ํƒ์ƒ‰
 14# =============================================================================
 15def binary_search(arr: List[int], target: int) -> int:
 16    """
 17    ์ •๋ ฌ๋œ ๋ฐฐ์—ด์—์„œ target์˜ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ
 18    ์—†์œผ๋ฉด -1 ๋ฐ˜ํ™˜
 19    ์‹œ๊ฐ„๋ณต์žก๋„: O(log n)
 20    """
 21    left, right = 0, len(arr) - 1
 22
 23    while left <= right:
 24        mid = left + (right - left) // 2  # ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๋ฐฉ์ง€
 25
 26        if arr[mid] == target:
 27            return mid
 28        elif arr[mid] < target:
 29            left = mid + 1
 30        else:
 31            right = mid - 1
 32
 33    return -1
 34
 35
 36def binary_search_recursive(arr: List[int], target: int, left: int, right: int) -> int:
 37    """์ด๋ถ„ ํƒ์ƒ‰ (์žฌ๊ท€ ๋ฒ„์ „)"""
 38    if left > right:
 39        return -1
 40
 41    mid = left + (right - left) // 2
 42
 43    if arr[mid] == target:
 44        return mid
 45    elif arr[mid] < target:
 46        return binary_search_recursive(arr, target, mid + 1, right)
 47    else:
 48        return binary_search_recursive(arr, target, left, mid - 1)
 49
 50
 51# =============================================================================
 52# 2. Lower Bound / Upper Bound
 53# =============================================================================
 54def lower_bound(arr: List[int], target: int) -> int:
 55    """
 56    target ์ด์ƒ์ธ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ์˜ ์ธ๋ฑ์Šค
 57    ๋ชจ๋“  ์š”์†Œ๊ฐ€ target๋ณด๋‹ค ์ž‘์œผ๋ฉด len(arr) ๋ฐ˜ํ™˜
 58    """
 59    left, right = 0, len(arr)
 60
 61    while left < right:
 62        mid = left + (right - left) // 2
 63        if arr[mid] < target:
 64            left = mid + 1
 65        else:
 66            right = mid
 67
 68    return left
 69
 70
 71def upper_bound(arr: List[int], target: int) -> int:
 72    """
 73    target ์ดˆ๊ณผ์ธ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ์˜ ์ธ๋ฑ์Šค
 74    ๋ชจ๋“  ์š”์†Œ๊ฐ€ target ์ดํ•˜์ด๋ฉด len(arr) ๋ฐ˜ํ™˜
 75    """
 76    left, right = 0, len(arr)
 77
 78    while left < right:
 79        mid = left + (right - left) // 2
 80        if arr[mid] <= target:
 81            left = mid + 1
 82        else:
 83            right = mid
 84
 85    return left
 86
 87
 88def count_occurrences(arr: List[int], target: int) -> int:
 89    """ํŠน์ • ๊ฐ’์˜ ๋“ฑ์žฅ ํšŸ์ˆ˜"""
 90    return upper_bound(arr, target) - lower_bound(arr, target)
 91
 92
 93# =============================================================================
 94# 3. ์ฒซ ๋ฒˆ์งธ/๋งˆ์ง€๋ง‰ ์œ„์น˜ ์ฐพ๊ธฐ
 95# =============================================================================
 96def find_first_position(arr: List[int], target: int) -> int:
 97    """target์ด ์ฒ˜์Œ ๋“ฑ์žฅํ•˜๋Š” ์ธ๋ฑ์Šค (์—†์œผ๋ฉด -1)"""
 98    idx = lower_bound(arr, target)
 99    if idx < len(arr) and arr[idx] == target:
100        return idx
101    return -1
102
103
104def find_last_position(arr: List[int], target: int) -> int:
105    """target์ด ๋งˆ์ง€๋ง‰์œผ๋กœ ๋“ฑ์žฅํ•˜๋Š” ์ธ๋ฑ์Šค (์—†์œผ๋ฉด -1)"""
106    idx = upper_bound(arr, target) - 1
107    if idx >= 0 and arr[idx] == target:
108        return idx
109    return -1
110
111
112# =============================================================================
113# 4. ํšŒ์ „ ์ •๋ ฌ ๋ฐฐ์—ด ๊ฒ€์ƒ‰
114# =============================================================================
115def search_rotated(arr: List[int], target: int) -> int:
116    """
117    ํšŒ์ „๋œ ์ •๋ ฌ ๋ฐฐ์—ด์—์„œ ๊ฒ€์ƒ‰
118    ์˜ˆ: [4, 5, 6, 7, 0, 1, 2] - ์›๋ž˜ [0,1,2,4,5,6,7]์„ ํšŒ์ „
119    ์‹œ๊ฐ„๋ณต์žก๋„: O(log n)
120    """
121    if not arr:
122        return -1
123
124    left, right = 0, len(arr) - 1
125
126    while left <= right:
127        mid = left + (right - left) // 2
128
129        if arr[mid] == target:
130            return mid
131
132        # ์™ผ์ชฝ ์ ˆ๋ฐ˜์ด ์ •๋ ฌ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ
133        if arr[left] <= arr[mid]:
134            if arr[left] <= target < arr[mid]:
135                right = mid - 1
136            else:
137                left = mid + 1
138        # ์˜ค๋ฅธ์ชฝ ์ ˆ๋ฐ˜์ด ์ •๋ ฌ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ
139        else:
140            if arr[mid] < target <= arr[right]:
141                left = mid + 1
142            else:
143                right = mid - 1
144
145    return -1
146
147
148# =============================================================================
149# 5. ์ตœ์†Ÿ๊ฐ’ ์ฐพ๊ธฐ (ํšŒ์ „ ๋ฐฐ์—ด)
150# =============================================================================
151def find_minimum_rotated(arr: List[int]) -> int:
152    """ํšŒ์ „ ์ •๋ ฌ ๋ฐฐ์—ด์˜ ์ตœ์†Ÿ๊ฐ’ ์ฐพ๊ธฐ"""
153    left, right = 0, len(arr) - 1
154
155    while left < right:
156        mid = left + (right - left) // 2
157
158        if arr[mid] > arr[right]:
159            left = mid + 1
160        else:
161            right = mid
162
163    return arr[left]
164
165
166# =============================================================================
167# 6. ์ œ๊ณฑ๊ทผ ๊ตฌํ•˜๊ธฐ (์ •์ˆ˜)
168# =============================================================================
169def integer_sqrt(n: int) -> int:
170    """
171    n์˜ ์ •์ˆ˜ ์ œ๊ณฑ๊ทผ (๋ฒ„๋ฆผ)
172    ์˜ˆ: sqrt(8) = 2
173    """
174    if n < 0:
175        raise ValueError("์Œ์ˆ˜์˜ ์ œ๊ณฑ๊ทผ์€ ์ •์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค")
176    if n == 0:
177        return 0
178
179    left, right = 1, n
180
181    while left <= right:
182        mid = left + (right - left) // 2
183        square = mid * mid
184
185        if square == n:
186            return mid
187        elif square < n:
188            left = mid + 1
189        else:
190            right = mid - 1
191
192    return right  # floor(sqrt(n))
193
194
195# =============================================================================
196# 7. ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ์ƒ‰ (Parametric Search)
197# =============================================================================
198def can_split(arr: List[int], m: int, max_sum: int) -> bool:
199    """
200    ๋ฐฐ์—ด์„ m๊ฐœ ์ดํ•˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋ˆŒ ๋•Œ
201    ๊ฐ ๊ทธ๋ฃน ํ•ฉ์ด max_sum ์ดํ•˜์ธ์ง€ ํ™•์ธ
202    """
203    count = 1
204    current_sum = 0
205
206    for num in arr:
207        if current_sum + num > max_sum:
208            count += 1
209            current_sum = num
210            if count > m:
211                return False
212        else:
213            current_sum += num
214
215    return True
216
217
218def split_array_min_largest_sum(arr: List[int], m: int) -> int:
219    """
220    ๋ฐฐ์—ด์„ m๊ฐœ์˜ ์—ฐ์† ๋ถ€๋ถ„ ๋ฐฐ์—ด๋กœ ๋‚˜๋ˆŒ ๋•Œ
221    ๊ฐ ๋ถ€๋ถ„ ๋ฐฐ์—ด ํ•ฉ์˜ ์ตœ๋Œ“๊ฐ’์„ ์ตœ์†Œํ™”
222    ์‹œ๊ฐ„๋ณต์žก๋„: O(n log(sum))
223    """
224    left = max(arr)      # ์ตœ์†Œ ๊ฐ€๋Šฅํ•œ ๊ฐ’: ๊ฐ€์žฅ ํฐ ์š”์†Œ
225    right = sum(arr)     # ์ตœ๋Œ€ ๊ฐ€๋Šฅํ•œ ๊ฐ’: ์ „์ฒด ํ•ฉ
226
227    while left < right:
228        mid = left + (right - left) // 2
229        if can_split(arr, m, mid):
230            right = mid
231        else:
232            left = mid + 1
233
234    return left
235
236
237# =============================================================================
238# 8. Peak Element ์ฐพ๊ธฐ
239# =============================================================================
240def find_peak_element(arr: List[int]) -> int:
241    """
242    ๋ฐฐ์—ด์—์„œ peak element์˜ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ
243    peak: arr[i] > arr[i-1] and arr[i] > arr[i+1]
244    ์‹œ๊ฐ„๋ณต์žก๋„: O(log n)
245    """
246    left, right = 0, len(arr) - 1
247
248    while left < right:
249        mid = left + (right - left) // 2
250
251        if arr[mid] > arr[mid + 1]:
252            right = mid
253        else:
254            left = mid + 1
255
256    return left
257
258
259# =============================================================================
260# ํ…Œ์ŠคํŠธ
261# =============================================================================
262def main():
263    print("=" * 60)
264    print("์ด๋ถ„ ํƒ์ƒ‰ (Binary Search) ์˜ˆ์ œ")
265    print("=" * 60)
266
267    # 1. ๊ธฐ๋ณธ ์ด๋ถ„ ํƒ์ƒ‰
268    print("\n[1] ๊ธฐ๋ณธ ์ด๋ถ„ ํƒ์ƒ‰")
269    arr = [1, 3, 5, 7, 9, 11, 13, 15]
270    target = 7
271    result = binary_search(arr, target)
272    print(f"    ๋ฐฐ์—ด: {arr}")
273    print(f"    {target} ์ฐพ๊ธฐ -> ์ธ๋ฑ์Šค: {result}")
274
275    # 2. Lower/Upper Bound
276    print("\n[2] Lower Bound / Upper Bound")
277    arr = [1, 2, 2, 2, 3, 4, 5]
278    target = 2
279    lb = lower_bound(arr, target)
280    ub = upper_bound(arr, target)
281    count = count_occurrences(arr, target)
282    print(f"    ๋ฐฐ์—ด: {arr}")
283    print(f"    target={target}")
284    print(f"    lower_bound: {lb}, upper_bound: {ub}")
285    print(f"    ๋“ฑ์žฅ ํšŸ์ˆ˜: {count}")
286
287    # bisect ๋ชจ๋“ˆ๊ณผ ๋น„๊ต
288    print(f"    (bisect_left: {bisect.bisect_left(arr, target)}, "
289          f"bisect_right: {bisect.bisect_right(arr, target)})")
290
291    # 3. ์ฒซ ๋ฒˆ์งธ/๋งˆ์ง€๋ง‰ ์œ„์น˜
292    print("\n[3] ์ฒซ ๋ฒˆ์งธ/๋งˆ์ง€๋ง‰ ์œ„์น˜")
293    arr = [5, 7, 7, 8, 8, 8, 10]
294    target = 8
295    first = find_first_position(arr, target)
296    last = find_last_position(arr, target)
297    print(f"    ๋ฐฐ์—ด: {arr}")
298    print(f"    {target}์˜ ์ฒซ ์œ„์น˜: {first}, ๋งˆ์ง€๋ง‰ ์œ„์น˜: {last}")
299
300    # 4. ํšŒ์ „ ๋ฐฐ์—ด ๊ฒ€์ƒ‰
301    print("\n[4] ํšŒ์ „ ์ •๋ ฌ ๋ฐฐ์—ด ๊ฒ€์ƒ‰")
302    arr = [4, 5, 6, 7, 0, 1, 2]
303    target = 0
304    result = search_rotated(arr, target)
305    print(f"    ํšŒ์ „ ๋ฐฐ์—ด: {arr}")
306    print(f"    {target} ์ฐพ๊ธฐ -> ์ธ๋ฑ์Šค: {result}")
307
308    # 5. ํšŒ์ „ ๋ฐฐ์—ด ์ตœ์†Ÿ๊ฐ’
309    print("\n[5] ํšŒ์ „ ๋ฐฐ์—ด ์ตœ์†Ÿ๊ฐ’")
310    arr = [4, 5, 6, 7, 0, 1, 2]
311    result = find_minimum_rotated(arr)
312    print(f"    ํšŒ์ „ ๋ฐฐ์—ด: {arr}")
313    print(f"    ์ตœ์†Ÿ๊ฐ’: {result}")
314
315    # 6. ์ œ๊ณฑ๊ทผ
316    print("\n[6] ์ •์ˆ˜ ์ œ๊ณฑ๊ทผ")
317    for n in [4, 8, 16, 17, 100]:
318        result = integer_sqrt(n)
319        print(f"    sqrt({n}) = {result}")
320
321    # 7. ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ์ƒ‰
322    print("\n[7] ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ์ƒ‰ (๋ฐฐ์—ด ๋ถ„ํ• )")
323    arr = [7, 2, 5, 10, 8]
324    m = 2
325    result = split_array_min_largest_sum(arr, m)
326    print(f"    ๋ฐฐ์—ด: {arr}, ๋ถ„ํ•  ์ˆ˜: {m}")
327    print(f"    ์ตœ์†Œ ์ตœ๋Œ€ํ•ฉ: {result}")
328
329    # 8. Peak Element
330    print("\n[8] Peak Element ์ฐพ๊ธฐ")
331    arr = [1, 2, 1, 3, 5, 6, 4]
332    result = find_peak_element(arr)
333    print(f"    ๋ฐฐ์—ด: {arr}")
334    print(f"    peak ์ธ๋ฑ์Šค: {result} (๊ฐ’: {arr[result]})")
335
336    print("\n" + "=" * 60)
337
338
339if __name__ == "__main__":
340    main()