탐색 알고리즘
이진 탐색 (Binary Search)
정렬된 배열에서 O(log n) 탐색.
# 기본
def binary_search(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = (lo + hi) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
# Lower bound — target 이상인 첫 번째 인덱스
def lower_bound(arr, target):
lo, hi = 0, len(arr)
while lo < hi:
mid = (lo + hi) // 2
if arr[mid] < target:
lo = mid + 1
else:
hi = mid
return lo
# Upper bound — target 초과인 첫 번째 인덱스
def upper_bound(arr, target):
lo, hi = 0, len(arr)
while lo < hi:
mid = (lo + hi) // 2
if arr[mid] <= target:
lo = mid + 1
else:
hi = mid
return lo
# Python 내장
import bisect
bisect.bisect_left(arr, target) # lower bound
bisect.bisect_right(arr, target) # upper bound투 포인터 (Two Pointer)
정렬된 배열에서 두 조건을 동시에 만족하는 쌍 탐색. O(n).
# 합이 target인 두 수 찾기
def two_sum(arr, target):
lo, hi = 0, len(arr) - 1
while lo < hi:
s = arr[lo] + arr[hi]
if s == target:
return (lo, hi)
elif s < target:
lo += 1
else:
hi -= 1
return None슬라이딩 윈도우
연속 구간의 최적값 탐색. O(n).
# 길이 k인 부분 배열의 최대 합
def max_sum_window(arr, k):
window_sum = sum(arr[:k])
max_sum = window_sum
for i in range(k, len(arr)):
window_sum += arr[i] - arr[i - k]
max_sum = max(max_sum, window_sum)
return max_sum
# 합이 target 이상인 최소 길이 부분 배열
def min_length_subarray(arr, target):
lo = total = 0
result = float('inf')
for hi in range(len(arr)):
total += arr[hi]
while total >= target:
result = min(result, hi - lo + 1)
total -= arr[lo]
lo += 1
return result if result != float('inf') else 0분할 정복 (Divide & Conquer)
# 거듭제곱 O(log n)
def power(base, exp):
if exp == 0: return 1
if exp % 2 == 0:
half = power(base, exp // 2)
return half * half
return base * power(base, exp - 1)
# 최대 부분 배열 (카데인 변형)
def max_crossing_sum(arr, lo, mid, hi):
left_sum = right_sum = float('-inf')
total = 0
for i in range(mid, lo-1, -1):
total += arr[i]
left_sum = max(left_sum, total)
total = 0
for i in range(mid+1, hi+1):
total += arr[i]
right_sum = max(right_sum, total)
return left_sum + right_sum백트래킹 (Backtracking)
가능한 모든 경우를 탐색하되, 조건에 맞지 않으면 되돌아간다.
# N-Queens
def solve_n_queens(n):
result = []
cols = set()
diag1 = set() # r - c
diag2 = set() # r + c
def backtrack(row, path):
if row == n:
result.append(path[:])
return
for col in range(n):
if col in cols or (row-col) in diag1 or (row+col) in diag2:
continue
cols.add(col); diag1.add(row-col); diag2.add(row+col)
path.append(col)
backtrack(row + 1, path)
path.pop()
cols.remove(col); diag1.remove(row-col); diag2.remove(row+col)
backtrack(0, [])
return result
# 순열
def permutations(nums):
result = []
def backtrack(path, used):
if len(path) == len(nums):
result.append(path[:])
return
for i, n in enumerate(nums):
if used[i]: continue
used[i] = True
path.append(n)
backtrack(path, used)
path.pop()
used[i] = False
backtrack([], [False] * len(nums))
return result