3.3. Демонстрация - Реализация бинарного поиска на практике
Шаг 1
Давайте напишем простенький бинарный поиск на примере из предыдущей статьи. На входе будем иметь отсортированный в алфавитном порядке список из растений. Напишем функцию, которая будет бинарным поиском искать позицию переданного в неё растения.
import {test} from "./tester";
/*
binarySearch(plants, "Пион") => 5
binarySearch(plants, "Роза") => null
*/
function binarySearch(plants, plant) {
return null;
}
const plants = [
"Аспарагус",
"Гвоздика",
"Жасмин",
"Калина",
"Малина",
"Пион",
"Тысячелистник",
"Хризантема",
"Шафран",
"Юкка",
]
test(binarySearch(plants, "Пион"), 5);
test(binarySearch(plants, "Роза"), null);
Шаг 2
Для начала обработаем самый простой случай: если то, что мы ищем, лежит ровно по центру — просто возвращаем индекс центра. Осталось написать всего ничего — смещение левой и правой границы поиска.
import {test} from "./tester";
/*
binarySearch(plants, "Пион") => 5
binarySearch(plants, "Роза") => null
*/
function binarySearch(plants, plant) {
const left = 0;
const right = plants.length - 1;
// Стандартная формула для поиска середины отрезка
const center = Math.floor((right + left) / 2);
if (center === plant) {
return center;
}
return null;
}
const plants = [
"Аспарагус",
"Гвоздика",
"Жасмин",
"Калина",
"Малина",
"Пион",
"Тысячелистник",
"Хризантема",
"Шафран",
"Юкка",
]
test(binarySearch(plants, "Пион"), 5);
test(binarySearch(plants, "Роза"), null);
Шаг 3
Вот и всё, первый бинарный поиск написан! Обратите внимание, что при смещении центра влево мы ищем от текущей левой границы до центра, не включая центр. То же самое касается и смещения вправо. С одной стороны, эта микрооптимизация поможет нам чуть быстрее смещать границы, ведь центр мы уже обработали. А с другой, гарантирует нам нарушение условия left < right, ведь если мы не найдем искомое в массиве из одного элемента, то одна из границ гарантированно зайдёт за другую.
import {test} from "./tester";
/*
binarySearch(plants, "Пион") => 5
binarySearch(plants, "Роза") => null
*/
function binarySearch(plants, plant) {
let left = 0;
let right = plants.length - 1;
while (left <= right) {
const center = Math.floor((right + left) / 2);
if (plants[center] === plant) {
return center;
}
// Если то, что мы ищем, левее по алфавиту, идем искать в левую часть массива
if (plants[center].localeCompare(plant) === 1) {
right = center - 1;
// иначе идем в другую сторону
} else {
left = center + 1;
}
}
return null;
}
const plants = [
"Аспарагус",
"Гвоздика",
"Жасмин",
"Калина",
"Малина",
"Пион",
"Тысячелистник",
"Хризантема",
"Шафран",
"Юкка",
]
test(binarySearch(plants, "Пион"), 5);
test(binarySearch(plants, "Роза"), null);
test(binarySearch(plants, "Аспарагус"), 0);
test(binarySearch(plants, "Хризантема"), 7);
test(binarySearch(plants, "Юкка"), 9);
File tester.js
const results = document.getElementById('results')
export function test(left, right) {
const result = document.createElement('li');
if (left === right) {
result.innerHTML = "Тест пройден!";
result.style.color = "green";
} else {
result.innerHTML = `Тест не пройден! ${left} не равно ${right}`;
result.style.color = "red";
}
results.appendChild(result);
}