* Add Russian docs site baseline * Add Russian localized codebase * Polish Russian code wording * Update ru code translation. * Update code translation and chapter covers. * Fix pythontutor extraction. * Add README and landing page. * placeholder of profiles * Use figures of English version * Remove chapter paperbook
5.1 KiB
Поисковая стратегия divide and conquer
Мы уже знаем, что алгоритмы поиска делятся на две большие категории.
- Полный перебор: реализуется через обход структуры данных, временная сложность равна
O(n). - Адаптивный поиск: использует особую организацию данных или априорную информацию, временная сложность может достигать
O(\log n)и дажеO(1).
На практике алгоритмы поиска с временной сложностью O(\log n) обычно реализуются на основе стратегии divide and conquer, например двоичный поиск и деревья.
- На каждом шаге двоичный поиск раскладывает задачу (поиск целевого элемента в массиве) на более мелкую задачу (поиск целевого элемента в одной половине массива), и этот процесс продолжается, пока массив не станет пустым или пока не будет найден целевой элемент.
- Деревья являются типичными представителями идей divide and conquer; в таких структурах данных, как двоичное дерево поиска, AVL-дерево и куча, временная сложность различных операций равна
O(\log n).
Стратегия divide and conquer для двоичного поиска выглядит следующим образом.
- Задача раскладывается на части: двоичный поиск рекурсивно разбивает исходную задачу (поиск в массиве) на подзадачу (поиск в одной половине массива), и это достигается сравнением среднего элемента с целевым значением.
- Подзадачи независимы: в двоичном поиске на каждом шаге обрабатывается только одна подзадача, и она не зависит от других подзадач.
- Решения подзадач не нужно объединять: двоичный поиск нацелен на поиск конкретного элемента, поэтому объединять решения подзадач не требуется. Как только подзадача решена, одновременно считается решенной и исходная задача.
По сути divide and conquer повышает эффективность поиска потому, что при полном переборе за один шаг удается исключить только один вариант, тогда как при поиске на основе divide and conquer за один шаг можно исключить половину вариантов.
Реализация двоичного поиска на основе divide and conquer
В предыдущих главах двоичный поиск реализовывался через итерацию. Теперь реализуем его с помощью divide and conquer, то есть через рекурсию.
!!! question
Дан отсортированный массив `nums` длины $n$ , в котором все элементы уникальны. Найдите элемент `target` .
С точки зрения divide and conquer обозначим подзадачу, соответствующую интервалу поиска [i, j] , через f(i, j) .
Начиная с исходной задачи f(0, n-1) , выполняем двоичный поиск по следующим шагам.
- Вычислить середину
mинтервала поиска[i, j]и с ее помощью исключить половину интервала. - Рекурсивно решить подзадачу вдвое меньшего размера; это может быть либо
f(i, m-1), либоf(m+1, j). - Повторять шаг
1.и шаг2., пока не будет найденtargetили пока интервал не станет пустым.
На рисунке ниже показан процесс применения divide and conquer для поиска элемента 6 в массиве.
В реализации кода мы объявляем рекурсивную функцию dfs() для решения задачи f(i, j) :
[file]{binary_search_recur}-[class]{}-[func]{binary_search}
