47 KiB
Поиск
{width="3.5416633858267716in" height="4.583333333333333in"}
двоичный поиск
Двоичный (бинарный) поиск -- это эффективный алгоритм поиска, основан- ный на стратегии «разделяй и властвуй». Он использует упорядоченность дан- ных, сокращая на каждом шаге область поиска вдвое, пока не будет найден целевой элемент или область поиска не станет пустой.
Рис. 10.1. Пример данных для двоичного поиска
Как показано на рис. 10.2, сначала инициализируются указатели i = 0 и j = n − 1, которые указывают на первый и последний элементы массива и представляют область поиска [0, n − 1]. Обратите внимание, что квадратные скобки обозначают замкнутый интервал, включающий граничные значения.
Затем в цикле выполняются следующие два шага:
-
вычисляется индекс средней точки m = ⌊(i + j)/2⌋, где ⌊ ⌋ обозначает опера- цию округления вниз;
-
определяется соотношение между nums[m] и target, выделяются три случая:
-
если nums[m] < target, то target находится в интервале [m + 1, j], поэтому выполняется i = m + 1;
-
если nums[m] > target, то target находится в интервале [i, m − 1], поэтому выполняется j = m − 1;
-
если nums[m] = target, то target найден, и возвращается индекс m.
-
Если массив не содержит целевой элемент, область поиска в конечном итоге сократится до пустой. В этом случае возвращается −1.
Рис. 10.2. Процесс двоичного поиска. Шаги 1--3
Рис. 10.2. Продолжение. Шаги 4--6
Рис. 10.2. Окончание. Шаг 7
Следует отметить, что, поскольку i и j имеют тип int, сумма i + j может превысить допустимый диапазон значений типа int. Чтобы избежать переполнения, обычно для вычисления средней точки используется формула m = ⌊i + (j − i)/2⌋.
Ниже приведен пример кода.
# === File: binary_search.py ===
def binary_search(nums: list[int], target: int) -> int: """ Двоичный поиск (двойной замкнутый интервал)."""
# Инициализация двойного замкнутого интервала [0, n-1], i и j указывают
# на первый и последний элементы массива. i, j = 0, len(nums) - 1
# Цикл, выход при пустом интервале поиска (когда i > j). while i <= j:
# Теоретически числа в Python могут быть бесконечно большими (зависит
# от объема памяти), и нет необходимости учитывать переполнение. m = (i + j) // 2 # Вычисление индекса средней точки m.
if nums[m] < target:
i = m + 1 # В этом случае target находится в интервале [m+1, j]. elif nums[m] > target:
j = m - 1 # В этом случае target находится в интервале [i, m-1].
else:
return m # Найден целевой элемент, возвращается его индекс. return -1 # Целевой элемент не найден, возвращается -1.
Временная сложность составляет O(log n): в цикле двоичного поиска об- ласть поиска сокращается вдвое на каждом шаге, поэтому количество итера- ций равно log2 n.
Пространственная сложность составляет O(1): указатели i и j занимают
постоянное количество памяти.
- Методы представления интервалов
Кроме указанного выше двойного замкнутого интервала, существует также ле- возамкнутый правооткрытый интервал [0, n), т. е. левая граница включается, а правая -- нет. В этом представлении интервал [i, j) пуст, когда i = j.
На основе этого представления можно реализовать двоичный поиск с ана- логичной функциональностью.
# === File: binary_search.py ===
def binary_search_lcro(nums: list[int], target: int) -> int:
""" Двоичный поиск (левозамкнутый правооткрытый интервал)."""
# Инициализация левозамкнутого правооткрытого интервала [0, n), i и j ука- зывают на первый элемент массива и элемент после последнего.
i, j = 0, len(nums)
# Цикл, выход при пустом интервале поиска (когда i = j). while i < j:
m = (i + j) // 2 # Вычисление индекса средней точки m. if nums[m] < target:
i = m + 1 # В этом случае target находится в интервале [m+1, j). elif nums[m] > target:
j = m # В этом случае target находится в интервале [i, m).
else:
return m # Найден целевой элемент, возвращается его индекс. return -1 # Целевой элемент не найден, возвращается -1.
В двух представлениях интервалов инициализация, условия цикла и опера- ции сокращения интервала в алгоритме двоичного поиска различаются, как показано на рис. 10.3.
Поскольку в представлении «двойной замкнутый интервал» обе границы определены как замкнутые, операции сокращения интервала с помощью указа- телей i и j также симметричны. Это снижает вероятность ошибок, поэтому обыч- но рекомендуется использовать запись «двойной замкнутый интервал».
Индекс
Обе границы включены в интервал
Интервал поиска: двойной замкнутый [i, j] Инициализация указателей: i = 0, j = n − 1 Условие завершения цикла: i > j
Сужение интервала: i = m + 1, j = m − 1
Интервал поиска: левозамкнутый правооот- крытый [i, j)
Инициализация указателей: i = 0, j = n Условие завершения цикла: i ≥ j
Операция сужения интервала: i = m + 1, j = m
Рис. 10.3. Два определения интервалов
Преимущества и ограничения
Двоичный поиск обладает хорошей производительностью как по времени, так и по пространству.
-
Двоичный поиск отличается высокой эффективностью по времени. При большом объеме данных логарифмическая временная сложность име- ет значительное преимущество. Например, при размере данных n = 220 линейный поиск требует 220 = 1 048 576 итераций, тогда как двоичный поиск -- всего log2 220 = 20 итераций.
-
Двоичный поиск, в отличие от некоторых других алгоритмов поиска (на-
пример, хеш-поиска), не требует дополнительного пространства и по- этому более экономичен в плане использования памяти.
Тем не менее двоичный поиск не подходит для всех случаев по следующим основным причинам.
-
Двоичный поиск применим только к упорядоченным данным. Если входные данные неупорядоченные, то их сортировка специально для использования двоичного поиска не оправдана. Это связано с тем, что временная сложность алгоритмов сортировки обычно составляет O(n log n), что выше, чем у линейного и двоичного поиска. В сценари- ях с частыми добавлениями элементов для поддержания упорядочен- ности массива необходимо вставлять элементы в определенные по- зиции, что имеет временную сложность O(n) и также является весьма затратным.
-
Двоичный поиск применим только к массивам, поскольку требует скач- кообразного (непрерывного) доступа к элементам. В связных списках выполнение скачкообразного доступа менее эффективно, поэтому такой поиск не подходит для применения в связных списках и структурах дан- ных, основанных на них.
-
При небольших объемах данных линейный поиск более эффективен. В линейном поиске на каждом этапе требуется только одна операция сравнения; в двоичном поиске требуется одна операция сложения, одна операция деления, от одной до трех операций сравнения и одна опера- ция сложения (вычитания), всего от четырех до шести элементарных операций. Поэтому, когда объем данных n невелик, линейный поиск ока- зывается быстрее двоичного.
вставка с использованием двоичного поиска
Двоичный поиск можно использовать не только для поиска целевого элемен- та, но и для решения множества других задач, таких как поиск позиции для вставки целевого элемента.
Случай без повторяющихся элементов
Рис. 10.4. Пример данных для вставки с использованием двоичного поиска
Если требуется повторно использовать код двоичного поиска из предыду- щего раздела, необходимо ответить на следующие два вопроса:
-
если массив содержит target, является ли индекс вставки индексом этого элемента? Условие задачи требует вставить target слева от равного эле- мента, т. е. новый target заменяет старое положение target. То есть если массив уже содержит target, индекс вставки совпадает с индексом этого target;
-
если массив не содержит target, какой элемент будет иметь индекс вставки?
Дальнейший процесс двоичного поиска: когда nums[m] < target, указатель i перемещается, т. е. приближается к элементу, большему или равному target. Аналогично указатель j всегда приближается к элементу, меньшему или рав- ному target.
Таким образом, по окончании двоичного поиска указатель i указывает на первый элемент, больший target, а указатель j -- на первый элемент, меньший target. Легко понять, что, если массив не содержит target, индекс вставки будет равен i. Ниже приведен пример кода.
# === File: binary_search_insertion.py ===
def binary_search_insertion_simple(nums: list[int], target: int) -> int: """ Двоичный поиск точки вставки (без повторяющихся элементов)."""
i, j = 0, len(nums) - 1 # Инициализация двойного закрытого интервала [0, n-1]. while i <= j:
m = (i + j) // 2 # Вычисление среднего индекса m. if nums[m] < target:
i = m + 1 # target находится в интервале [m+1, j]. elif nums[m] > target:
j = m - 1 # target находится в интервале [i, m-1].
else:
return m # Найден target, возвращается точка вставки m.
# target не найден, возвращается точка вставки i. return i
Случай с повторяющимися элементами
Если в массиве существует несколько одинаковых target, то обычный двоич- ный поиск может вернуть индекс только одного из них, не определяя, сколько target находится слева и справа от этого элемента.
Задача требует вставить целевой элемент в самое левое положение, поэто- му необходимо найти индекс самого левого target в массиве. Первоначально предполагается реализовать решение следующим образом (см. рис. 10.5):
-
выполнить двоичный поиск и получить индекс любого target, обозна- чить его как k;
-
начиная с индекса k, выполнить линейный обход влево и вернуть ин- декс, когда будет найден самый левый target.
Рис. 10.5. Линейный поиск точки вставки для повторяющихся элементов
Это рабочий метод, но он включает линейный поиск, поэтому его времен- ная сложность составляет O(n). Когда в массиве много повторяющихся target, эффективность этого метода низка.
Теперь рассмотрим расширение алгоритма двоичного поиска. Общий про- цесс остается неизменным: на каждом этапе сначала вычисляется средний ин- декс m, затем определяется отношение между target и nums[m], как показано на рис. 10.6. Возможны два случая:
-
nums[m] < target или nums[m] > target, тогда target еще не найден, поэтому используется операция сужения интервала обычного двоичного поиска, чтобы указатели i и j приближались к target;
-
nums[m] == target, тогда элементы, меньшие target, находятся в интервале [i, m -- 1]. Поэтому используется операция j = m -- 1 для сужения интерва- ла, чтобы указатель j приблизился к элементам, меньшим target.
После завершения цикла i будет указывать на самый левый target, а j -- на первый элемент, меньший target. Поэтому индекс i является точкой вставки.
Рис. 10.6. Этапы двоичного поиска точки вставки для повторяющихся элементов. Шаги 1--2
Рис. 10.6. Продолжение. Шаги 3--5
Рис. 10.6. Окончание. Шаги 6--8
Ниже приведен пример кода. Операции в ветвях nums[m] > target и nums[m] == target одинаковы, поэтому их можно объединить. Тем не менее условие можно оставить развернутым, так как это делает логику более ясной и улучшает чи- таемость.
# === File: binary_search_insertion.py ===
def binary_search_insertion(nums: list[int], target: int) -> int:
""" Двоичный поиск точки вставки (с повторяющимися элементами)."""
i, j = 0, len(nums) - 1 # Инициализация двойного закрытого интервала [0, n-1].
while i <= j:
m = (i + j) // 2 # Вычисление индекса середины m. if nums[m] < target:
i = m + 1 # target в интервале [m+1, j]. elif nums[m] > target:
j = m - 1 # target в интервале [i, m-1].
else:
j = m - 1 # Первый элемент, меньший target, в интервале [i, m-1]. # Возврат точки вставки i.
return i
Подводя итоги, можно сказать, что двоичный поиск заключается в установке целей поиска для указателей i и j. Целью может быть конкретный элемент (на- пример, target) или диапазон элементов (например, элементы, меньшие target). В процессе повторяющегося двоичного поиска указатели i и j постепенно приближаются к заранее установленной цели. В конечном итоге они либо
успешно находят ответ, либо останавливаются после выхода за границы.
двоичный поиск границ
Поиск левой границы
- Двоичный поиск границ ❖ 277
Вспомним метод двоичного поиска точки вставки: после завершения поис- ка индекс i указывает на самый левый элемент target, поэтому поиск точки вставки, по сути, является поиском индекса самого левого target.
Рассмотрим реализацию поиска левой границы через функцию поиска точ- ки вставки. Обратите внимание, что массив может не содержать target, что мо- жет привести к следующим двум результатам:
-
индекс точки вставки i выходит за границы;
-
элемент nums[i] не равен target.
При возникновении этих двух ситуаций следует сразу вернуть --1. Код реа- лизации приведен ниже.
# === File: binary_search_edge.py ===
def binary_search_left_edge(nums: list[int], target: int) -> int: """ Двоичный поиск самого левого элемента target."""
# Эквивалентно поиску точки вставки target. i = binary_search_insertion(nums, target)
# target не найден, возвращается -1.
if i == len(nums) or nums[i] != target: return -1
# target найден, возвращается индекс i. return i
Поиск правой границы
Как найти самый правый элемент target? Самый очевидный способ -- изменить код, заменив операцию сужения указателя в случае nums[m] == target. Мы не будем приводить код для этого случая, заинтересованные читатели могут реа- лизовать его самостоятельно.
Ниже представлены два более изящных подхода.
Повторное использование поиска левой границы
На самом деле можно использовать функцию поиска самого левого элемента для поиска самого правого элемента. Что именно нужно сделать: преобразо- вать поиск самого правого элемента target в поиск самого левого target + 1.
После завершения поиска указатель i указывает на самый левый элемент target + 1 (если он существует), а j указывает на самый правый target, поэтому можно вернуть j, как показано на рис. 10.7.
Рис. 10.7. Преобразование поиска правой границы в поиск левой границы
Обратите внимание, что возвращаемая точка вставки -- это i, поэтому необ- ходимо вычесть 1, чтобы получить j.
# === File: binary_search_edge.py ===
def binary_search_right_edge(nums: list[int], target: int) -> int: """ Двоичный поиск самого правого target."""
# Преобразование в поиск самого левого target + 1. i = binary_search_insertion(nums, target + 1)
# j указывает на самый правый target, i указывает на первый элемент,
# больший target. j = i - 1
# target не найден, возвращается -1. if j == -1 or nums[j] != target:
return -1
# target найден, возвращается индекс j. return j
Преобразование в поиск элемента
Известно, что, когда массив не содержит элемент target, индексы i и j в конеч- ном итоге указывают на первый элемент, больший target, и на первый эле- мент, меньший target, соответственно.
Таким образом, для поиска левой и правой границ можно создать элемент, отсутствующий в массиве, как показано на рис. 10.8.
- Поиск самого левого target: можно преобразовать в поиск target - 0.5
и вернуть указатель i.
- Поиск самого правого target: можно преобразовать в поиск target + 0.5
и вернуть указатель j.
Рис. 10.8. Преобразование поиска границ в поиск элемента
Код мы не приводим, но стоит обратить внимание на следующие два мо- мента:
-
данный массив не содержит дробных чисел, т. е. не нужно беспокоиться об обработке случаев равенства другим элементам массива;
-
поскольку этот метод вводит дробные числа, необходимо изменить тип переменной target на тип с плавающей запятой (в Python это изменение не требуется).
Стратегии оптимизации хеширования
В алгоритмических задачах линейный поиск часто заменяется на хеш- поиск, чтобы снизить временную сложность алгоритма. Рассмотрим за- дачу для углубленного понимания этого приема.
Линейный поиск: обмен времени на пространство
Рассмотрим прямой перебор всех возможных комбинаций. Мы запускаем два вложенных цикла и на каждой итерации проверяем, равна ли сумма двух це- лых чисел target. Если да, то возвращаем их индексы, см. рис. 10.9.
Рис. 10.9. Линейный поиск для нахождения двух чисел, сумма которых равна заданному
Ниже приведен код реализации.
# === File: two_sum.py ===
def two_sum_brute_force(nums: list[int], target: int) -> list[int]: """ Метод 1: Полный перебор."""
# Два вложенных цикла, временная сложность O(n^2). for i in range(len(nums) - 1):
for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target:
return [i, j]
return []
Временная сложность этого метода составляет O(n2), а пространственная сложность O(1), что делает его крайне медленным при большом объеме данных.
Хеш-поиск: обмен пространства на время
Рассмотрим использование хеш-таблицы, в которой ключами и значениями являются элементы массива и их индексы. Циклически обходим массив, вы- полняя следующие шаги, показанные на рис. 10.10:
-
проверить, содержится ли число target - nums[i] в хеш-таблице. Если да, то сразу вернуть индексы этих двух элементов;
-
добавить в хеш-таблицу пару ключ--значение: nums[i] и i.
Рис. 10.10. Использование вспомогательной хеш-таблицы для нахождения двух чисел, сумма которых равна заданному. Шаги 1--2
Рис. 10.10. Окончание. Шаг 3
Код реализации представлен ниже, требуется только один цикл.
# === File: two_sum.py ===
def two_sum_hash_table(nums: list[int], target: int) -> list[int]: """ Метод 2: Вспомогательная хеш-таблица."""
# Вспомогательная хеш-таблица, пространственная сложность O(n). dic = {}
# Один цикл, временная сложность O(n). for i in range(len(nums)):
if target - nums[i] in dic:
return [dic[target - nums[i]], i] dic[nums[i]] = i
return []
Этот метод снижает временную сложность с O(n2) до O(n) благодаря хеш- поиску, значительно повышая эффективность выполнения.
Поскольку требуется поддерживать дополнительную хеш-таблицу, про- странственная сложность составляет O(n). Тем не менее общая эффектив- ность этого метода более сбалансирована, что делает его оптимальным решением данной задачи.
переосмысление алгоритмов поиска
Алгоритмы поиска используются для нахождения одного или нескольких эле- ментов, удовлетворяющих определенным условиям, в структурах данных, та- ких как массивы, списки, деревья или графы.
Алгоритмы поиска можно классифицировать по принципу их реализации на следующие категории.
-
Поиск целевого элемента путем обхода структуры данных, напри- мер обход массива, списка, дерева и графа.
-
Эффективный поиск элементов с использованием структуры орга- низации данных или априорной информации, например двоичный поиск, хеш-поиск и поиск в двоичных деревьях.
Нетрудно заметить, что эти темы уже были рассмотрены в предыдущих гла- вах, поэтому алгоритмы поиска нам уже знакомы. В этом разделе мы система- тизируем полученные ранее знания.
Полный перебор
Полный перебор заключается в обходе каждого элемента структуры данных для нахождения целевого элемента.
-
Линейный поиск применяется к линейным структурам данных, таким как массивы и списки. Он начинается с одного конца структуры данных и последовательно проверяет элементы, пока не будет найден целевой элемент или не будет достигнут другой конец.
-
Поиск в ширину и поиск в глубину -- это две стратегии обхода графов и деревьев. Поиск в ширину начинается с начального узла и исследу- ет все узлы на текущем уровне перед переходом на следующий. Поиск в глубину начинается с начального узла и следует по пути до конца, за- тем возвращается и пробует другие пути, пока не будет полностью прой- дена вся структура данных.
Преимущество полного перебора заключается в его простоте и универсаль- ности, так как он не требует предварительной обработки данных и ис- пользования дополнительных структур данных.
Однако временная сложность таких алгоритмов составляет O(n), где n -- количество элементов, что делает их менее эффективными при большом объ- еме данных.
Адаптивный поиск
Адаптивный поиск использует специфические свойства данных (например, упорядоченность) для оптимизации процесса поиска, что позволяет более эф- фективно находить целевой элемент.
-
Двоичный поиск использует упорядоченность данных для эффективно- го поиска и применим только к массивам.
-
Хеш-поиск использует хеш-таблицы для создания отображения между данными поиска и целевыми данными, что позволяет эффективно вы- полнять операции поиска.
-
Поиск в дереве осуществляется в определенной структуре дерева (на- пример, в двоичном дереве поиска) путем сравнения значений узлов для быстрого исключения узлов и нахождения целевого элемента.
Преимуществом таких алгоритмов является высокая эффективность, вре- менная сложность может достигать O(log n) и даже O(1).
Однако использование этих алгоритмов часто требует предваритель- ной обработки данных. Например, для двоичного поиска необходимо пред-
варительно отсортировать массив, хеш-поиск и поиск в дереве требуют ис- пользования дополнительных структур данных, поддержание которых также требует дополнительных временных и пространственных затрат.
Выбор метода поиска
Для поиска целевого элемента в заданном наборе данных размером n можно использовать различные методы, такие как линейный поиск, двоичный поиск, поиск в дереве, хеш-поиск и др. Принципы работы каждого метода показаны на рис. 10.11.
Рис. 10.11. Различные стратегии поиска
Эффективность и характеристики указанных методов приведены в табл. 10.1.
+---------------------+----------------------+----------------+----------------------+-----------------+ | | > Линейный поиск | > Двоичный | > Поиск в дереве | > Хеш-поиск | +=====================+======================+================+======================+=================+ | > Поиск элемента | > O(n) | > O(log n) | > O(log n) | > O(1) | +---------------------+----------------------+----------------+----------------------+-----------------+ | > Вставка элемента | > O(1) | > O(n) | > O(log n) | > O(1) | +---------------------+----------------------+----------------+----------------------+-----------------+ | > Удаление элемента | > O(n) | > O(n) | > O(log n) | > O(1) | +---------------------+----------------------+----------------+----------------------+-----------------+ | > Дополнительное | > O(1) | > O(1) | > O(n) | > O(n) | +---------------------+----------------------+----------------+----------------------+-----------------+
поиск
пространство
+------------------------------------+----------------------+--------------------+----------------------------------------+------------------------------------+ | | | | | > Окончание табл. 10.1 | +====================================+======================+:==================:+========================================+:==================================:+ | | > Линейный поиск | > Двоичный | > Поиск в дереве | > Хеш-поиск | +------------------------------------+----------------------+--------------------+----------------------------------------+------------------------------------+ | > Предварительная обработка данных | > -- | > Сортировка | > Построение де- рева O(n log n) | > Построение хеш- таблицы O(n) | | | | > | | | | | | > O(n log n) | | | +------------------------------------+----------------------+--------------------+----------------------------------------+------------------------------------+ | > Упорядоченность данных | > Неупорядо- ченные | > Упорядочен- | > Упорядоченные | > Неупорядоченные | +------------------------------------+----------------------+--------------------+----------------------------------------+------------------------------------+
ные
Выбор алгоритма поиска также зависит от объема данных, требований к производительности поиска, частоты запросов и обновлений данных.
Линейный поиск
-
Обладает хорошей универсальностью, не требует предварительной об- работки данных. Если необходимо выполнить только один запрос, вре- мя предварительной обработки данных для других трех методов будет дольше, чем время линейного поиска.
-
Подходит для небольших объемов данных, в этом случае временная сложность мало влияет на эффективность.
-
Подходит для сценариев с высокой частотой обновления данных, так как этот метод не требует дополнительного обслуживания данных.
Двоичный поиск
-
Подходит для больших объемов данных, демонстрирует стабильную эф- фективность, худшая временная сложность составляет O(log n).
-
Объем данных не должен быть слишком большим, так как массив требует непрерывного пространства в памяти.
-
Не подходит для сценариев с частыми добавлениями и удалениями дан- ных, так как поддержание упорядоченного массива требует значитель- ных затрат.
Хеш-поиск
-
Подходит для сценариев с высокими требованиями к производительно- сти поиска, средняя временная сложность составляет O(1).
-
Не подходит для случаев, когда требуется упорядоченность данных или поиск по диапазону, так как хеш-таблица не может поддерживать упоря- доченность данных.
-
Сильно зависит от хеш-функции и стратегии обработки коллизий, суще- ствует значительный риск ухудшения производительности.
-
Не подходит для слишком больших объемов данных, так как хеш-таблица требует дополнительного пространства для минимизации коллизий и обеспечения хорошей производительности поиска.
Поиск в дереве
-
Подходит для огромных объемов данных, так как узлы дерева хранятся в памяти раздельно.
-
Подходит для случаев, когда требуется поддержание упорядоченности данных или поиск по диапазону.
-
В процессе постоянного добавления и удаления узлов двоичное дерево поиска может стать несбалансированным, и временная сложность ухуд- шится до O(n).
-
Если используется АВЛ-дерево или красно-черное дерево, все операции могут выполняться со стабильной эффективностью O(log n), но операции по поддержанию баланса дерева увеличивают дополнительные затраты.
резюме
-
Двоичный поиск требует упорядоченности данных и выполняется путем циклического сокращения области поиска в два раза. Требует упорядо- ченности входных данных и подходит только для массивов или структур данных, основанных на массивах.
-
Полный перебор осуществляется путем обхода структуры данных для нахождения целевого значения. Линейный поиск подходит для массивов и списков, поиск в ширину и поиск в глубину подходят для графов и де- ревьев. Эти алгоритмы обладают хорошей универсальностью, не требу- ют предварительной обработки данных, но их временная сложность O(n) достаточно высока.
-
Хеш-поиск, поиск в деревьях и двоичный поиск относятся к эффек- тивным методам поиска, которые позволяют быстро находить целевой элемент в определенных структурах данных. Эти алгоритмы отличают- ся высокой эффективностью, их временная сложность может достигать O(log n) или даже O(1), однако обычно они требуют использования до- полнительных структур данных.
-
На практике для выбора подходящего метода необходимо проводить конкретный анализ таких факторов, как объем данных, требования к производительности поиска, частота запросов и обновлений данных.
-
Линейный поиск подходит для небольших или часто обновляемых дан- ных. Двоичный поиск -- для больших, отсортированных данных. Хеш- поиск -- когда важна высокая эффективность запросов и не требуется поиск диапазонов. Поиск в деревьях -- для больших динамических дан- ных, в которых необходимо поддерживать порядок и выполнять запросы диапазонов.
-
Замена линейного поиска на хеш-поиск является распространенной стратегией оптимизации времени выполнения, позволяющей снизить временную сложность с O(n) до O(1).
Глава 11
























