This commit is contained in:
krahets
2026-04-03 18:46:15 +08:00
parent 377736b1bd
commit 9d21ca86b0
352 changed files with 46563 additions and 11262 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ comments: true
# 10.1   Двоичный поиск
<u>Двоичный поиск (binary search)</u> - это эффективный алгоритм поиска, основанный на стратегии "разделяй и властвуй". Он использует упорядоченность данных и на каждом шаге сокращает область поиска вдвое, пока не найдет целевой элемент или пока интервал поиска не опустеет.
<u>Двоичный поиск (binary search)</u> - это эффективный алгоритм поиска, основанный на стратегии "разделяй и властвуй". Он использует упорядоченность данных, сокращая на каждом шаге область поиска вдвое, пока не будет найден целевой элемент или пока интервал поиска не опустеет.
!!! question
@@ -373,7 +373,7 @@ comments: true
## 10.1.1 &nbsp; Методы представления интервалов
Помимо описанного выше двойного замкнутого интервала, часто используется и интервал "слева закрыт, справа открыт", который задается как $[0, n)$ , то есть левая граница включается, а правая - нет. В этом представлении интервал $[i, j)$ пуст, когда $i = j$ .
Помимо описанного выше двойного замкнутого интервала, часто используется и левозамкнутый правооткрытый интервал, который задается как $[0, n)$ , то есть левая граница включается, а правая - нет. В этом представлении интервал $[i, j)$ пуст, когда $i = j$ .
На основе этого представления можно реализовать двоичный поиск с той же функциональностью:
@@ -10,7 +10,7 @@ comments: true
Дан упорядоченный массив `nums` длины $n$, который может содержать повторяющиеся элементы. Верните индекс самого левого элемента `target` в массиве. Если массив не содержит этот элемент, верните $-1$ .
Вспомним метод поиска точки вставки при двоичном поиске: после завершения поиска указатель $i$ указывает на самый левый `target` , **поэтому поиск точки вставки по сути и есть поиск индекса самого левого `target`**.
Вспомним метод поиска точки вставки при двоичном поиске: после завершения поиска указатель $i$ указывает на самый левый `target` , **поэтому поиск точки вставки по сути является поиском индекса самого левого `target`**.
Рассмотрим реализацию поиска левой границы через функцию поиска точки вставки. Обратите внимание: массив может не содержать `target` , и тогда возможны две ситуации.
@@ -2,7 +2,7 @@
comments: true
---
# 10.2 &nbsp; Точка вставки при двоичном поиске
# 10.2 &nbsp; Двоичный поиск точки вставки
Двоичный поиск можно использовать не только для поиска целевого элемента, но и для решения многих вариаций задачи, например для поиска позиции вставки целевого элемента.
@@ -24,7 +24,7 @@ comments: true
**Вопрос 2**: если массив не содержит `target` , индекс какого элемента будет точкой вставки?
Рассмотрим процесс двоичного поиска подробнее: когда `nums[m] < target` , указатель $i$ сдвигается, а значит, приближается к элементу, который больше либо равен `target` . Аналогично указатель $j$ все время приближается к элементу, который меньше либо равен `target` .
Рассмотрим процесс двоичного поиска подробнее: когда `nums[m] < target` , указатель $i$ сдвигается вправо и тем самым приближается к элементу, который больше либо равен `target` . Аналогично указатель $j$ постепенно приближается к элементу, который меньше либо равен `target` .
Следовательно, после завершения двоичного поиска обязательно выполняется следующее: указатель $i$ указывает на первый элемент, больший `target` , а указатель $j$ указывает на первый элемент, меньший `target` . **Нетрудно сделать вывод, что если массив не содержит `target` , то индекс вставки равен $i$** . Код приведен ниже:
@@ -372,7 +372,7 @@ comments: true
<p align="center"> Рисунок 10-6 &nbsp; Шаги поиска точки вставки для повторяющихся элементов </p>
Если посмотреть на следующий код, то видно, что операции в ветвях `nums[m] > target` и `nums[m] == target` совпадают, поэтому эти две ветви можно объединить.
Если посмотреть на следующий код, то видно, что действия в ветвях `nums[m] > target` и `nums[m] == target` совпадают, поэтому эти две ветви можно объединить.
Даже в этом случае можно оставить условия развернутыми, потому что так логика выглядит более ясной и код легче читать.
@@ -669,6 +669,6 @@ comments: true
Код в этом разделе записан в стиле "двойного замкнутого интервала". При желании можно самостоятельно реализовать вариант "слева закрыт, справа открыт".
Если смотреть в целом, суть двоичного поиска сводится к тому, что для указателей $i$ и $j$ заранее задаются цели поиска; целью может быть конкретный элемент (например, `target` ), а может быть и диапазон элементов (например, элементы, меньшие `target` ).
Если смотреть в целом, суть двоичного поиска сводится к тому, что для указателей $i$ и $j$ заранее задаются ориентиры поиска; целью может быть конкретный элемент, например `target` , а может быть и диапазон элементов, например все элементы, меньшие `target` .
В ходе непрерывного двоичного деления указатели $i$ и $j$ постепенно приближаются к заранее заданной цели. В конце они либо успешно находят ответ, либо останавливаются после выхода за границы.
+6 -6
View File
@@ -9,15 +9,15 @@ icon: material/text-search
!!! abstract
Поиск - это приключение в неизвестность: иногда приходится пройти каждый уголок загадочного пространства, а иногда удается быстро зафиксировать цель.
Поиск - это движение в неизвестность: иногда приходится пройти каждый уголок пространства, а иногда удается быстро найти цель.
В этом путешествии каждый новый шаг может привести к ответу, которого мы не ожидали.
В этом пути каждый новый шаг может привести к ответу, которого мы не ожидали.
## Содержание главы
- [10.1 &nbsp; Двоичный поиск](binary_search.md)
- [10.2 &nbsp; Точка вставки двоичного поиска](binary_search_insertion.md)
- [10.3 &nbsp; Граничные случаи двоичного поиска](binary_search_edge.md)
- [10.4 &nbsp; Стратегия оптимизации через хеширование](replace_linear_by_hashing.md)
- [10.5 &nbsp; Алгоритмы поиска: новый взгляд](searching_algorithm_revisited.md)
- [10.2 &nbsp; Двоичный поиск точки вставки](binary_search_insertion.md)
- [10.3 &nbsp; Двоичный поиск границ](binary_search_edge.md)
- [10.4 &nbsp; Стратегии оптимизации хеширования](replace_linear_by_hashing.md)
- [10.5 &nbsp; Переосмысление алгоритмов поиска](searching_algorithm_revisited.md)
- [10.6 &nbsp; Резюме](summary.md)
@@ -2,7 +2,7 @@
comments: true
---
# 10.4 &nbsp; Стратегии хеш-оптимизации
# 10.4 &nbsp; Стратегии оптимизации хеширования
В алгоритмических задачах **мы часто заменяем линейный поиск на хеш-поиск, чтобы уменьшить временную сложность алгоритма**. Разберем одну задачу, чтобы лучше понять этот прием.
@@ -11,14 +11,14 @@ comments: true
- **Поиск целевого элемента путем обхода структуры данных**, например обход массива, списка, дерева или графа.
- **Эффективный поиск элементов с использованием структуры организации данных или априорной информации**, например двоичный поиск, хеш-поиск и поиск в двоичном дереве поиска.
Нетрудно заметить, что эти темы уже рассматривались в предыдущих главах, поэтому алгоритмы поиска нам уже знакомы. В этом разделе мы еще раз посмотрим на них, но уже более системно.
Нетрудно заметить, что эти темы уже рассматривались в предыдущих главах, поэтому алгоритмы поиска нам уже знакомы. В этом разделе мы систематизируем полученные ранее знания и еще раз посмотрим на них как на единую группу методов.
## 10.5.1 &nbsp; Полный перебор
Полный перебор заключается в том, что мы обходим каждый элемент структуры данных, чтобы найти целевой элемент.
- "Линейный поиск" применяется к линейным структурам данных, таким как массивы и списки. Он начинается с одного конца структуры данных и последовательно проверяет элементы, пока не найдет целевой элемент или пока не достигнет другого конца структуры данных.
- "Поиск в ширину" и "поиск в глубину" - это две стратегии обхода графов и деревьев. Поиск в ширину стартует из начального узла и исследует все узлы текущего уровня, прежде чем переходить к следующему. Поиск в глубину стартует из начального узла, проходит один путь до конца, затем возвращается назад и пробует другие пути, пока не будет полностью пройдена вся структура данных.
- "Обход в ширину" и "обход в глубину" - это две стратегии обхода графов и деревьев. Обход в ширину стартует из начального узла и исследует все узлы текущего уровня, прежде чем переходить к следующему. Обход в глубину стартует из начального узла, проходит один путь до конца, затем возвращается назад и пробует другие пути, пока не будет полностью пройдена вся структура данных.
Преимущество полного перебора состоит в его простоте и универсальности, **поскольку он не требует предварительной обработки данных и использования дополнительных структур данных**.
@@ -32,13 +32,13 @@ comments: true
- "Хеш-поиск" использует хеш-таблицу для построения отображения между поисковыми данными и целевыми данными, благодаря чему запросы выполняются эффективно.
- "Поиск в дереве" ведется в конкретной древовидной структуре (например, в двоичном дереве поиска) и позволяет быстро отсекать узлы на основе сравнения значений, чтобы найти цель.
Преимущество этих алгоритмов в высокой эффективности, **их временная сложность может достигать $O(\log n)$ и даже $O(1)$** .
Преимущество этих алгоритмов заключается в высокой эффективности: **их временная сложность может достигать $O(\log n)$ и даже $O(1)$** .
Однако **для использования таких алгоритмов обычно требуется предварительная обработка данных**. Например, для двоичного поиска нужно заранее отсортировать массив, а хеш-поиск и поиск в дереве требуют дополнительных структур данных, поддержание которых тоже отнимает время и память.
!!! tip
Адаптивные алгоритмы поиска часто называют алгоритмами поиска в узком смысле, **поскольку они в основном предназначены для быстрого поиска целевого элемента в конкретной структуре данных**.
Адаптивные алгоритмы поиска часто называют алгоритмами поиска в узком смысле, **поскольку они в основном предназначены для быстрого нахождения целевого элемента в конкретной структуре данных**.
## 10.5.3 &nbsp; Выбор метода поиска
+1 -1
View File
@@ -7,7 +7,7 @@ comments: true
### 1. &nbsp; Ключевые выводы
- Двоичный поиск опирается на упорядоченность данных и выполняет поиск путем циклического сокращения интервала вдвое. Он требует упорядоченных входных данных и подходит только для массивов или структур данных, реализованных на их основе.
- Полный перебор находит данные путем обхода структуры данных. Линейный поиск подходит для массивов и списков, а поиск в ширину и поиск в глубину подходят для графов и деревьев. Эти алгоритмы универсальны и не требуют предварительной обработки данных, но их временная сложность $O(n)$ сравнительно велика.
- Полный перебор находит данные путем обхода структуры данных. Линейный поиск подходит для массивов и списков, а обход в ширину и обход в глубину подходят для графов и деревьев. Эти алгоритмы универсальны и не требуют предварительной обработки данных, но их временная сложность $O(n)$ сравнительно велика.
- Хеш-поиск, поиск в дереве и двоичный поиск относятся к эффективным методам поиска и позволяют быстро находить целевой элемент в конкретных структурах данных. Такие алгоритмы обладают высокой эффективностью, их временная сложность может достигать $O(\log n)$ и даже $O(1)$ , но обычно им нужны дополнительные структуры данных.
- На практике нужно анализировать размер данных, требования к производительности поиска, а также частоту запросов и обновлений данных, чтобы выбрать подходящий метод поиска.
- Линейный поиск подходит для небольших или часто обновляемых наборов данных; двоичный поиск - для больших отсортированных данных; хеш-поиск - для сценариев с высокими требованиями к скорости запросов и без необходимости поиска по диапазону; поиск в дереве - для больших динамических данных, где нужно поддерживать порядок и выполнять диапазонные запросы.