Двоичный (бинарный) поиск, BinSearch, БинПоиск
Поиск в упорядоченном массиве за $O(\log{n})$. Так же его называют - метод деления пополам и дихотомия (деление пополам по-гречески).
Перед применением двоичного поиска нужно отсортировать массив одним из алгоритмов сортировки.
Цель
Найти элемент со значением $x$ в отсортированном массиве $A$ из $N$ элементов или установить, что элемента $x$ в массиве $A$ нет.
Идея
Разделить отсортированный массив на две половины, сравнить средний элемент с $x$, понять в какой половине массива может находиться значение $x$ и перейти к поиску в этой половине.. И так далее, пока размер массива не уменьшиться до 1 элемента, тогда либо этот элемент равен $x$ и мы нашли $x$, либо не равен $x$, и тогда элемента $x$ нет в массиве $A$.
Скорость работы
Линейный поиск (последовательный просмотр всех элементов массива) выполняется за $O(N)$ операций, двоичный поиск - за $O(\log{N})$.
Пример реализации на C++
Шаблонная функция и пример её использования:
template <typename T> int binary_serch (const T *a,int n ,const T &elem);
Пусть $a$ - отсортированный массив, тогда функция за логарифмическое время возвращает $i$-ый индекс элемента
массива
elem=a[i] и -1, если элемент не найден.
#include <iostream> // Отсюда будем использовать вывод на экран при потоков (cout)
#include <algorithm> // Алгоритм сортировки (функция sort) из STL
#include <stdlib.h> // Функция rand() - случайные числа
#include <ctime> // Функция time() - время
#include <conio.h> // Функция getch() - ожидание нажатия клавиши в конце программы
// Шаблонная функция
template <typename T> int binary_search(const T *a, int n, const T &elem){
int L = 0, R = n-1; // Левая и правая границы поиска
while (L < R){ // Пока в рассматриваемом куске массива больше одного элемента
int m = (L+R)/2; // Вычисляем индекс среднего элемента
if (elem <= a[m]) // Если искомый элемент меньше центрального
R = m; // Сдвигаем правую границу влево
else // , а иначе
L = m+1; // Левую границу вправо
}
// Остался один элемент с индеком L = R,
// и это либо искомый элемент elem, либо elem в массиве вообще нет!
return (a[R] == elem) ? R : -1; // возвращаем индекс или -1 если элемент не найден
}
using namespace std; // Чтобы не писать перед каждым cout "std::"
// Основная программа c примером использования
int main() {
// Создаём случайный массив из N элементов
const int N = 20;
int a[N];
// Заполняем массив a случайными целыми числами
srand(time(0)); // Инициализация генератора случайных чисел
for(int i=0;i<N;i++)
a[i] = rand() % 200; // случайные числа в диапазоне 0..199
int el1 = a[rand() % N]; // Запоминаем случайный элемент массива
// он точно будет в отсортированном массиве :)
// Сортируем массив a при помощи функции sort из STL
sort(a, a+N); // Да-да.. вот так просто, одной строкой, можно отсортировать массив ;)
// Выводим массив на экран
for(int i=0;i<N;i++)
cout << "a[" << i << "] = " << a[i] << endl;
// Поиск элементов массива при помощи функции binary_search
// Ищем элемент массива, который там заведомо есть
cout << "search " << el1 << " " << binary_search(a,N,el1) << endl;
// Поиск элемента, которого в массиве a заведомо нет, т.к. он больше 199
cout << "search " << 1000 << " " << binary_search(a,N,1000) << endl;
cout << "Press any key..."; getch(); // Ожидаем нажатия клавиши :)
return 0;
}
Стандартная STL функция binary_search возвращает лишь найден элемент или нет.
#include <iostream> // Вывод на экран
#include <algorithm> // Алгоритм сортировки из STL
#include <stdlib.h> // Функция rand() - случайные числа
#include <ctime> // Функция time() - время
// Основная программа для демонстрации функции
using namespace std; // Чтобы не писать везде "std::"
int main() {
// Создаём случайный массив из N элементов
const int N = 20;
int a[N];
// Заполняем случайными числами
srand(time(0)); // Инициализация генератора случаных чисел
for(int i=0;i<N;i++)
a[i] = rand() % 200;
int el1 = a[rand() % N]; // Запоминаем элемент массива
// Сортируем массив a
sort(a, a+N);
// Выводим массив на экран
for(int i=0;i<N;i++)
cout << "a[" << i << "] = " << a[i] << endl;
// Поиск элементов массива
// Ищем элемент массива, который там заведомо есть
cout << "search " << el1 << " " << binary_search(a,a+N,el1) << endl;
cout << "search " << 1000 << " " << binary_search(a,a+N,1000) << endl;
return 0;
}
Реализация на Python
# Двоичный поиск в отсортированном списке: O(log n) по времени, O(1) по памяти
def binary_search(items, target):
"""Возвращает индекс элемента target в отсортированном списке или -1."""
left, right = 0, len(items) - 1
while left <= right:
middle = (left + right) // 2
if items[middle] == target:
return middle
if items[middle] < target:
left = middle + 1 # искомое значение правее середины
else:
right = middle - 1 # искомое значение левее середины
return -1
def main():
numbers = [1, 3, 5, 7, 9, 11, 13, 15]
for target in (7, 8, 1, 15):
print(f"search {target}: index {binary_search(numbers, target)}")
if __name__ == "__main__":
main()
Реализация на Rust
// Двоичный поиск в отсортированном срезе: O(log n) по времени, O(1) по памяти
pub fn binary_search(items: &[i64], target: i64) -> Option<usize> {
let mut left = 0;
let mut right = items.len();
while left < right {
let middle = left + (right - left) / 2;
match items[middle].cmp(&target) {
std::cmp::Ordering::Equal => return Some(middle),
std::cmp::Ordering::Less => left = middle + 1, // искомое значение правее середины
std::cmp::Ordering::Greater => right = middle, // искомое значение левее середины
}
}
None
}
fn main() {
let numbers = [1, 3, 5, 7, 9, 11, 13, 15];
for target in [7, 8, 1, 15] {
match binary_search(&numbers, target) {
Some(index) => println!("search {target}: index {index}"),
None => println!("search {target}: not found"),
}
}
}
Интерактивно: двоичный поиск
Введите отсортированный массив и искомое число. На каждом шаге показан средний элемент и отрезок, в котором ещё может быть искомое.
Реализация на Delphi
Добавить картинки
{$APPTYPE CONSOLE}
uses SysUtils;
procedure swap(var a, b: int64);
var t: int64;
begin
t := a; a := b; b := t;
end;
var
n, i, j, m, k, ans, l, r: integer;
a: array [1..1000000] of int64;
procedure qsort(l, r: integer);
var
i, j: integer;
m: int64;
begin
i := l;
j := r;
m := a[random(r - l + 1) + l];
while i <= j do begin
while a[i] < m do inc(i);
while a[j] > m do dec(j);
if i <= j then begin
swap(a[i], a[j]);
inc(i);
dec(j);
end;
end;
if i < r then qsort(i, r);
if l < j then qsort(l, j);
end;
begin
assign(input, 'find.in'); reset(input);
assign(output, 'find.out'); rewrite(output);
read(n, l);
for i := 1 to n do
read(a[i]);
qsort(1, n);
for j := 1 to l do begin
read(k);
l := 1;
r := n + 1;
While r - l > 1 do begin
m := (r + l) div 2;
if k >= a[m] then l := m else r := m;
end;
if k = a[l] then writeln('YES') else writeln('NO');
end;
end.