mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-16 21:50:59 +00:00
First version.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# Поиск границ с использованием двоичного поиска
|
||||
|
||||
## Поиск левой границы
|
||||
|
||||
!!! question
|
||||
|
||||
Дан упорядоченный массив `nums` длиной $n$, который может содержать повторяющиеся элементы. Необходимо вернуть индекс самого левого элемента `target` в массиве. Если массив не содержит этот элемент, вернуть $-1$.
|
||||
|
||||
Вспомним метод поиска точки вставки с использованием двоичного поиска: после завершения поиска $i$ указывает на самый левый `target`, **поэтому поиск точки вставки по сути является поиском индекса самого левого** `target`.
|
||||
|
||||
Рассмотрим реализацию поиска левой границы через функцию поиска точки вставки. Обратите внимание, что массив может не содержать `target`, что может привести к следующим двум результатам:
|
||||
|
||||
- Индекс точки вставки $i$ выходит за границы массива.
|
||||
- Элемент `nums[i]` не равен `target`.
|
||||
|
||||
При возникновении любой из этих двух ситуаций можно просто вернуть $-1$. Код приведен ниже:
|
||||
|
||||
```src
|
||||
[file]{binary_search_edge}-[class]{}-[func]{binary_search_left_edge}
|
||||
```
|
||||
|
||||
## Поиск правой границы
|
||||
|
||||
Как же найти самый правый `target`? Самый прямой способ -- изменить код, заменив операцию сужения указателей в случае `nums[m] == target`. Код здесь опущен, заинтересованные читатели могут реализовать его самостоятельно.
|
||||
|
||||
Ниже мы рассмотрим два более изящных метода.
|
||||
|
||||
### Повторное использование поиска левой границы
|
||||
|
||||
На самом деле мы можем использовать функцию поиска самого левого элемента для поиска самого правого элемента, конкретный метод: **преобразовать поиск самого правого** `target` **в поиск самого левого** `target + 1`.
|
||||
|
||||
Как показано на рисунке ниже, после завершения поиска указатель $i$ указывает на самый левый `target + 1` (если он существует), а $j$ указывает на самый правый `target`, **поэтому достаточно вернуть** $j$.
|
||||
|
||||

|
||||
|
||||
Обратите внимание, что возвращаемая точка вставки -- это $i$, поэтому необходимо вычесть из нее $1$, чтобы получить $j$:
|
||||
|
||||
```src
|
||||
[file]{binary_search_edge}-[class]{}-[func]{binary_search_right_edge}
|
||||
```
|
||||
|
||||
### Преобразование в поиск элемента
|
||||
|
||||
Мы знаем, что когда массив не содержит `target`, в конечном итоге $i$ и $j$ будут указывать соответственно на первый элемент, больший и меньший `target`.
|
||||
|
||||
Поэтому, как показано на рисунке ниже, мы можем создать элемент, которого не существует в массиве, для поиска левой и правой границ.
|
||||
|
||||
- Поиск самого левого `target`: можно преобразовать в поиск `target - 0.5` и вернуть указатель $i$.
|
||||
- Поиск самого правого `target`: можно преобразовать в поиск `target + 0.5` и вернуть указатель $j$.
|
||||
|
||||

|
||||
|
||||
Код здесь опущен, следует отметить следующие два момента:
|
||||
|
||||
- Данный массив не содержит дробных чисел, что означает, что нам не нужно беспокоиться о том, как обрабатывать случай равенства.
|
||||
- Поскольку этот метод вводит дробные числа, необходимо изменить тип переменной `target` в функции на тип с плавающей точкой (в Python изменения не требуются).
|
||||
Reference in New Issue
Block a user