Изучаем структуры данных и алгоритмы в Python

Раздел: Python -> Алгоритмы и структуры данных

Алгоритмы поиска в Python

Задача поиска элемента в коллекции данных встречается повсеместно. Выбор подходящего алгоритма определяет скорость работы программы. Рассмотрим основные подходы на примере поиска числа в списке.

Как эффективно найти элемент в отсортированном списке за время O(log n)?

Бинарный поиск (binary search) последовательно делит диапазон поиска пополам, сравнивая искомое значение со средним элементом. Это оптимальный способ для упорядоченных данных.

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

# Пример использования
numbers = [2, 5, 8, 12, 16, 23, 38, 56]
result = binary_search(numbers, 23)
print(f"Индекс числа 23: {result}")

алгоритм задачи python (алгоритмы задач на python)

Индекс числа 23: 5

проверка на простое число python (проверка числа на простоту в python (алгоритм))

Пояснение:

  • Инициализируем границы left и right.
  • Пока границы не пересеклись, находим середину и сравниваем.
  • Возвращаем индекс или -1, если элемент отсутствует.

Как найти элемент без отсортированности списка?

Линейный поиск перебирает все элементы последовательно. Он прост, но неэффективен на больших объёмах.

def linear_search(arr, target):
    for i, val in enumerate(arr):
        if val == target:
            return i
    return -1

# Пример
numbers = [5, 2, 8, 12, 1]
print(linear_search(numbers, 8))

Python алгоритмы языка (алгоритмы в python)

2

алгоритмы с примерами на python (примеры алгоритмов на python)

Типичная ошибка: Использование бинарного поиска на неотсортированном массиве даёт неверный результат. Всегда проверяйте, отсортированы ли данные.

Как использовать встроенный модуль bisect для бинарного поиска?

Стандартная библиотека Python предоставляет модуль bisect, который реализует бинарный поиск для вставки и поиска.

import bisect

arr = [10, 20, 30, 40, 50]
pos = bisect.bisect_left(arr, 30)
if pos < len(arr) and arr[pos] == 30:
    print(f"Элемент найден на индексе {pos}")
else:
    print("Не найден")
Элемент найден на индексе 2

Примечание: bisect_left возвращает индекс первого вхождения, bisect_right – последнего.

Если элемент отсутствует, bisect_left вернёт позицию, куда его можно вставить. Не забывайте проверять равенство.

Как реализовать рекурсивный бинарный поиск?

Рекурсивная версия более декларативна, но может превысить глубину рекурсии для больших массивов.

def binary_search_recursive(arr, target, left, right):
    if left > right:
        return -1
    mid = (left + right) // 2
    if arr[mid] == target:
        return mid
    elif arr[mid] < target:
        return binary_search_recursive(arr, target, mid + 1, right)
    else:
        return binary_search_recursive(arr, target, left, mid - 1)

arr = [1, 3, 5, 7, 9]
print(binary_search_recursive(arr, 7, 0, len(arr)-1))
3

Проблема: Для списка из миллиона элементов глубина рекурсии может превысить лимит (~1000 в Python). Решение – использовать итеративную версию или увеличить лимит через sys.setrecursionlimit.

Алгоритмы сортировки

Как быстро отсортировать список с минимальным кодом?

Встроенная функция sorted() или метод list.sort() используют Timsort – гибридный алгоритм с временем O(n log n). Это самый надёжный вариант для реальных задач.

data = [3, 1, 4, 1, 5, 9, 2, 6]
data_sorted = sorted(data)
print(data_sorted)
data.sort()
print(data)
[1, 1, 2, 3, 4, 5, 6, 9]
[1, 1, 2, 3, 4, 5, 6, 9]

Как написать быструю сортировку (quicksort) для обучения?

Quicksort выбирает опорный элемент и рекурсивно сортирует подмассивы. Реализация короткая, но на практике уступает Timsort.

def quicksort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quicksort(left) + middle + quicksort(right)

print(quicksort([7, 2, 1, 6, 8, 5, 3, 4]))
[1, 2, 3, 4, 5, 6, 7, 8]

Ошибка: Использование дополнительной памяти O(n) из-за создания новых списков. Профессиональная реализация сортирует на месте (in-place). Также плохой выбор опоры может привести к O(n^2).

Рекурсивные алгоритмы: пример чисел Фибоначчи

Как вычислить n-е число Фибоначчи с мемоизацией?

Простая рекурсия без запоминания приводит к экспоненциальному времени. Мемоизация (кэширование) даёт O(n).

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

print(fib(10))
print(fib(50))
55
12586269025

Пояснение:

Декоратор lru_cache автоматически сохраняет результаты вызовов.

Как обойтись без рекурсии для больших n?

Итеративный подход с циклом использует O(1) памяти и не подвержен ограничению глубины.

def fib_iter(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

print(fib_iter(100))
354224848179261915075

Проблема: Рекурсия с большими n вызывает RecursionError. Всегда рассматривайте итеративную альтернативу, если глубина может превысить 1000.

Обход графа: BFS

Как найти кратчайший путь в невзвешенном графе?

Поиск в ширину (BFS) использует очередь. Он гарантирует нахождение кратчайшего пути по числу рёбер.

from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    visited.add(start)
    while queue:
        vertex = queue.popleft()
        print(vertex, end=' ')
        for neighbour in graph[vertex]:
            if neighbour not in visited:
                visited.add(neighbour)
                queue.append(neighbour)

graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}
bfs(graph, 'A')
A B C D E F

Ошибка: Забыть отметить вершину как посещённую перед добавлением в очередь – приведёт к бесконечному циклу. Проверяйте visited сразу.

Расширенные примеры и модификации

Поиск первого и последнего вхождения элемента в отсортированном массиве

Для дублирующихся элементов нужно найти границы. Используем два бинарных поиска.

Пример
def find_first(arr, target):
    left, right = 0, len(arr) - 1
    result = -1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            result = mid
            right = mid - 1
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return result

def find_last(arr, target):
    left, right = 0, len(arr) - 1
    result = -1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            result = mid
            left = mid + 1
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return result

arr = [1, 2, 2, 2, 3, 4, 5]
print(f"Первое вхождение 2: {find_first(arr, 2)}")
print(f"Последнее вхождение 2: {find_last(arr, 2)}")
Первое вхождение 2: 1
Последнее вхождение 2: 3

Бинарный поиск в повёрнутом массиве

Дан отсортированный массив, который был циклически сдвинут. Необходимо найти элемент.

Пример
def search_rotated(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        if nums[left] <= nums[mid]:
            # левая часть отсортирована
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        else:
            # правая часть отсортирована
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1

rotated = [4, 5, 6, 7, 0, 1, 2]
print(f"Индекс 0: {search_rotated(rotated, 0)}")
print(f"Индекс 3: {search_rotated(rotated, 3)}")
Индекс 0: 4
Индекс 3: -1

Сортировка слиянием (merge sort) с подробным разбором

Алгоритм делит массив пополам, рекурсивно сортирует половины и сливает их.

Пример
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

arr = [38, 27, 43, 3, 9, 82, 10]
sorted_arr = merge_sort(arr)
print(sorted_arr)
[3, 9, 10, 27, 38, 43, 82]

Проблема: Требуется дополнительная память O(n). Для больших массивов может быть критично. Альтернатива – сортировка на месте (heap sort).

Быстрое возведение в степень (бинарное возведение)

Алгоритм вычисляет a^n за O(log n) путём разложения степени по двоичному представлению.

Пример
def fast_pow(a, n):
    if n == 0:
        return 1
    if n % 2 == 0:
        half = fast_pow(a, n // 2)
        return half * half
    else:
        return a * fast_pow(a, n - 1)

print(fast_pow(2, 10))
print(fast_pow(3, 5))
1024
243

Поиск подстроки алгоритмом Кнута-Морриса-Пратта (KMP)

КМП ищет вхождения строки pattern в текст за O(n+m) без возврата.

Пример
def kmp_search(text, pattern):
    # построение префикс-функции
    n, m = len(text), len(pattern)
    lps = [0] * m
    length = 0
    i = 1
    while i < m:
        if pattern[i] == pattern[length]:
            length += 1
            lps[i] = length
            i += 1
        else:
            if length != 0:
                length = lps[length - 1]
            else:
                lps[i] = 0
                i += 1
    # поиск
    i = j = 0
    indices = []
    while i < n:
        if pattern[j] == text[i]:
            i += 1
            j += 1
        if j == m:
            indices.append(i - j)
            j = lps[j - 1]
        elif i < n and pattern[j] != text[i]:
            if j != 0:
                j = lps[j - 1]
            else:
                i += 1
    return indices

text = "ABABDABACDABABCABAB"
pattern = "ABABCABAB"
print(kmp_search(text, pattern))
[10]

Алгоритмы задач на Python - comments

En
алгоритм задачи python (python)