все Python 3Алгоритмы и структуры данныхпонятие
Бинарный поиск
поиск
Поиск в отсортированном массиве за O(log n). Каждый шаг делит область поиска пополам.
Синтаксис
def binary_search(arr, target):
Главная ловушка — данные обязаны быть отсортированы: на неотсортированном списке поиск не падает, а тихо возвращает неверный ответ. Ради одной проверки сортировать невыгодно: O(n log n) на сортировку дороже обычного оператора in за O(n), выигрыш появляется, когда по одному и тому же массиву ищут многократно. Писать его руками обычно незачем — bisect_left вернёт позицию вставки, останется сравнить элемент на этой позиции с искомым; переполнения (lo + hi), из-за которого этот алгоритм годами ломался в C и Java, в Python не бывает.
Примеры
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
print(binary_search([1,3,5,7,9,11], 7)) # → 3
print(binary_search([1,3,5,7,9,11], 6)) # → -1
# bisect — стандартная библиотека
import bisect
arr = [1,3,5,7,9]
print(bisect.bisect_left(arr, 5)) # → 2 (позиция для вставки)
print(bisect.bisect_right(arr, 5)) # → 3
# Рекурсивный вариант
def bs_rec(arr, target, lo=0, hi=None):
if hi is None: hi = len(arr)-1
if lo > hi: return -1
mid = (lo+hi)//2
if arr[mid] == target: return mid
elif arr[mid] < target: return bs_rec(arr, target, mid+1, hi)
else: return bs_rec(arr, target, lo, mid-1)
print(bs_rec([2,4,6,8,10], 6)) # → 2
# Поиск первого вхождения дубликата
def first_occurrence(arr, target):
lo, hi, result = 0, len(arr)-1, -1
while lo <= hi:
mid = (lo+hi)//2
if arr[mid] == target:
result = mid; hi = mid-1
elif arr[mid] < target: lo = mid+1
else: hi = mid-1
return result
print(first_occurrence([1,2,2,2,3], 2)) # → 1
# Бинарный поиск ответа
def min_pages(pages, students):
lo, hi = max(pages), sum(pages)
def can_split(limit):
count, cur = 1, 0
for p in pages:
if cur + p > limit: count+=1; cur=0
cur += p
return count <= students
while lo < hi:
mid = (lo+hi)//2
if can_split(mid): hi=mid
else: lo=mid+1
return lo
print(min_pages([10,20,30,40], 2)) # → 60
# bisect.insort — вставка в отсортированный список
import bisect
arr2 = [1,3,5,9]
bisect.insort(arr2, 6)
print(arr2) # → [1,3,5,6,9]
См. также
Открыть в полном глоссарииdocs.python.orgСообщить о неточности