package main
func binarySearch(arr []int, target int) int {
left, right := 0, len(arr)-1
for left <= right {
mid := left + (right-left)/2 // Overflow protection
if arr[mid] == target {
return mid
} else if arr[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
static int BinarySearch(int[] arr, int target)
{
int left = 0;
int right = arr.Length - 1;
while (left <= right)
{
int mid = left + (right - left) / 2; // Overflow protection
if (arr[mid] == target)
{
return mid;
}
else if (arr[mid] < target)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return -1;
}
def binary_search(arr: list[int], target: int) -> int:
left, right = 0, len(arr) - 1
while left <= right:
# Python ints are arbitrary precision, so (left + right) // 2 cannot
# overflow — the form below is kept for parity with other languages
mid = left + (right - left) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
package main
func searchRight(arr []int, target int) int {
left, right := 0, len(arr)-1
result := -1
for left <= right {
mid := left + (right-left)/2
if arr[mid] == target {
result = mid
left = mid + 1 // Keep searching right
} else if arr[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return result
}
static int SearchRight(int[] arr, int target)
{
int left = 0;
int right = arr.Length - 1;
int result = -1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] == target)
{
result = mid;
left = mid + 1; // Keep searching right
}
else if (arr[mid] < target)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return result;
}
def search_right(arr: list[int], target: int) -> int:
left, right = 0, len(arr) - 1
result = -1
while left <= right:
mid = left + (right - left) // 2
if arr[mid] == target:
result = mid
left = mid + 1 # Keep searching right
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return result
# The stdlib equivalent: bisect.bisect_right(arr, target) - 1
## Бинарный поиск на ответ
Когда прямой поиск невозможен, но можно проверить, подходит ли конкретное значение.
Шаблон: если функция монотонна (чем больше x, тем больше/меньше f(x)), то можно искать x бинарно.
Задача: Koko Eating Bananas
Условие: n кучек бананов, h часов. Какая минимальная скорость k (бананов в час)?
package main
import "slices"
func minEatingSpeed(piles []int, h int) int {
left, right := 1, slices.Max(piles)
for left < right {
mid := left + (right-left)/2
hours := 0
for _, p := range piles {
hours += (p + mid - 1) / mid // ceil(p / mid)
}
if hours <= h {
right = mid // Can go slower
} else {
left = mid + 1 // Need to go faster
}
}
return left
}
// piles = [3, 6, 7, 11], h = 8
// mid=7: hours = 1+1+1+2 = 5 <= 8, right=7
// mid=4: hours = 1+2+2+3 = 8 <= 8, right=4
// mid=2: hours = 2+3+4+6 = 15 > 8, left=3
// mid=3: hours = 1+2+3+4 = 10 > 8, left=4
// left == right == 4. Answer: 4
static int MinEatingSpeed(int[] piles, int h)
{
int left = 1;
int right = piles.Max();
while (left < right)
{
int mid = left + (right - left) / 2;
int hours = 0;
foreach (int p in piles)
{
hours += (p + mid - 1) / mid; // ceil(p / mid)
}
if (hours <= h)
{
right = mid; // Can go slower
}
else
{
left = mid + 1; // Need to go faster
}
}
return left;
}
// piles = [3, 6, 7, 11], h = 8
// mid=7: hours = 1+1+1+2 = 5 <= 8, right=7
// mid=4: hours = 1+2+2+3 = 8 <= 8, right=4
// mid=2: hours = 2+3+4+6 = 15 > 8, left=3
// mid=3: hours = 1+2+3+4 = 10 > 8, left=4
// left == right == 4. Answer: 4
def min_eating_speed(piles: list[int], h: int) -> int:
left, right = 1, max(piles)
while left < right:
mid = left + (right - left) // 2
# -(-p // mid) is exact integer ceiling division (no float rounding)
hours = sum(-(-p // mid) for p in piles)
if hours <= h:
right = mid # Can go slower
else:
left = mid + 1 # Need to go faster
return left
# piles = [3, 6, 7, 11], h = 8
# mid=7: hours = 1+1+1+2 = 5 <= 8, right=7
# mid=4: hours = 1+2+2+3 = 8 <= 8, right=4
# mid=2: hours = 2+3+4+6 = 15 > 8, left=3
# mid=3: hours = 1+2+3+4 = 10 > 8, left=4
# left == right == 4. Answer: 4
### Задача: Search in Rotated Sorted Array
<?php
declare(strict_types=1);
/**
* @param list<int> $nums
*/
function searchRotated(array $nums, int $target): int
{
$left = 0;
$right = count($nums) - 1;
while ($left <= $right) {
$mid = $left + intdiv($right - $left, 2);
if ($nums[$mid] === $target) {
return $mid;
}
// Determine which half is sorted
if ($nums[$left] <= $nums[$mid]) { // Left half is sorted
if ($nums[$left] <= $target && $target < $nums[$mid]) {
$right = $mid - 1;
} else {
$left = $mid + 1;
}
} else { // Right half is sorted
if ($nums[$mid] < $target && $target <= $nums[$right]) {
$left = $mid + 1;
} else {
$right = $mid - 1;
}
}
}
return -1;
}
// [4, 5, 6, 7, 0, 1, 2], target = 0
// mid=7: left sorted [4,5,6,7], 0 not in [4,7) -> right half
// mid=1: right sorted [0,1,2], 0 in (1,2] no -> left=mid+1
// Recalculate... mid=4(idx), val=0 = target -> return 4
package main
func searchRotated(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
}
// Determine which half is sorted
if nums[left] <= nums[mid] { // Left half is sorted
if nums[left] <= target && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else { // Right half is sorted
if nums[mid] < target && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
}
}
return -1
}
// [4, 5, 6, 7, 0, 1, 2], target = 0
// mid=7: left sorted [4,5,6,7], 0 not in [4,7) -> right half
// mid=1: right sorted [0,1,2], 0 in (1,2] no -> left=mid+1
// Recalculate... mid=4(idx), val=0 = target -> return 4
static int SearchRotated(int[] nums, int target)
{
int left = 0;
int right = nums.Length - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (nums[mid] == target)
{
return mid;
}
// Determine which half is sorted
if (nums[left] <= nums[mid]) // Left half is sorted
{
if (nums[left] <= target && target < nums[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
else // Right half is sorted
{
if (nums[mid] < target && target <= nums[right])
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
}
return -1;
}
// [4, 5, 6, 7, 0, 1, 2], target = 0
// mid=7: left sorted [4,5,6,7], 0 not in [4,7) -> right half
// mid=1: right sorted [0,1,2], 0 in (1,2] no -> left=mid+1
// Recalculate... mid=4(idx), val=0 = target -> return 4
def search_rotated(nums: list[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
# Determine which half is sorted
if nums[left] <= nums[mid]: # Left half is sorted
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else: # Right half is sorted
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
# [4, 5, 6, 7, 0, 1, 2], target = 0
# mid=7: left sorted [4,5,6,7], 0 not in [4,7) -> right half
# mid=1: right sorted [0,1,2], 0 in (1,2] no -> left=mid+1
# Recalculate... mid=4(idx), val=0 = target -> return 4
### Задача: Find Minimum in Rotated Array
<?php
declare(strict_types=1);
/**
* @param list<int> $nums
*/
function findMin(array $nums): int
{
$left = 0;
$right = count($nums) - 1;
while ($left < $right) {
$mid = $left + intdiv($right - $left, 2);
if ($nums[$mid] > $nums[$right]) {
$left = $mid + 1; // Minimum is on the right
} else {
$right = $mid; // Minimum is on the left (including mid)
}
}
return $nums[$left];
}
// [3, 4, 5, 1, 2]
// mid=5 > right=2 -> left=3 (idx)
// left == right -> nums[3] = 1
package main
func findMin(nums []int) int {
left, right := 0, len(nums)-1
for left < right {
mid := left + (right-left)/2
if nums[mid] > nums[right] {
left = mid + 1 // Minimum is on the right
} else {
right = mid // Minimum is on the left (including mid)
}
}
return nums[left]
}
// [3, 4, 5, 1, 2]
// mid=5 > right=2 -> left=3 (idx)
// left == right -> nums[3] = 1
static int FindMin(int[] nums)
{
int left = 0;
int right = nums.Length - 1;
while (left < right)
{
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right])
{
left = mid + 1; // Minimum is on the right
}
else
{
right = mid; // Minimum is on the left (including mid)
}
}
return nums[left];
}
// [3, 4, 5, 1, 2]
// mid=5 > right=2 -> left=3 (idx)
// left == right -> nums[3] = 1
def find_min(nums: list[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
mid = left + (right - left) // 2
if nums[mid] > nums[right]:
left = mid + 1 # Minimum is on the right
else:
right = mid # Minimum is on the left (including mid)
return nums[left]
# [3, 4, 5, 1, 2]
# mid=5 > right=2 -> left=3 (idx)
# left == right -> nums[3] = 1
package main
func mySqrt(x int) int {
if x < 2 {
return x
}
left, right := 1, x/2
for left <= right {
mid := left + (right-left)/2
square := mid * mid
if square == x {
return mid
} else if square < x {
left = mid + 1
} else {
right = mid - 1
}
}
return right // right = floor(sqrt(x))
}
static int MySqrt(int x)
{
if (x < 2)
{
return x;
}
int left = 1;
int right = x / 2;
while (left <= right)
{
int mid = left + (right - left) / 2;
int square = mid * mid;
if (square == x)
{
return mid;
}
else if (square < x)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return right; // right = floor(sqrt(x))
}
def my_sqrt(x: int) -> int:
if x < 2:
return x
left, right = 1, x // 2
while left <= right:
mid = left + (right - left) // 2
square = mid * mid
if square == x:
return mid
elif square < x:
left = mid + 1
else:
right = mid - 1
return right # right = floor(sqrt(x))
# The stdlib equivalent: math.isqrt(x)
// Template 1: Exact search
for left <= right {
mid := left + (right-left)/2
if arr[mid] == target { return mid }
if arr[mid] < target { left = mid + 1 } else { right = mid - 1 }
}
return -1
// Template 2: Left boundary (first true)
for left < right {
mid := left + (right-left)/2
if condition(mid) {
right = mid
} else {
left = mid + 1
}
}
return left
// Template 3: Right boundary (last true)
for left < right {
mid := left + (right-left+1)/2 // Round up!
if condition(mid) {
left = mid
} else {
right = mid - 1
}
}
return left
// Template 1: Exact search
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] == target) { return mid; }
if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; }
}
return -1;
// Template 2: Left boundary (first true)
while (left < right)
{
int mid = left + (right - left) / 2;
if (Condition(mid))
{
right = mid;
}
else
{
left = mid + 1;
}
}
return left;
// Template 3: Right boundary (last true)
while (left < right)
{
int mid = left + (right - left + 1) / 2; // Round up!
if (Condition(mid))
{
left = mid;
}
else
{
right = mid - 1;
}
}
return left;
# Template 1: Exact search
while left <= right:
mid = left + (right - left) // 2
if arr[mid] == target:
return mid
if arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
# Template 2: Left boundary (first true)
while left < right:
mid = left + (right - left) // 2
if condition(mid):
right = mid
else:
left = mid + 1
return left
# Template 3: Right boundary (last true)
while left < right:
mid = left + (right - left + 1) // 2 # Round up!
if condition(mid):
left = mid
else:
right = mid - 1
return left
## Типичные ловушки
Бесконечный цикл:while left < right с right = mid работает. Но left = mid без округления вверх зацикливается.
Integer overflow: используй mid = left + (right - left) / 2 вместо (left + right) / 2.
Off-by-one: определи четко, что означают left и right (включительно или нет).
Запомни: Бинарный поиск не только для отсортированных массивов. Если функция монотонна — можно искать на ответе. Три шаблона: точный поиск, левая граница, правая граница. На интервью: если видишь O(n) решение и данные отсортированы — подумай о бинарном поиске для O(log n).
Итоги
Классический: left <= right, O(log n)
Левая/правая граница: left < right, разное направление сужения
Бинарный поиск на ответ: проверяем condition(mid)
Rotated array: определи отсортированную половину
Защита от overflow: mid = left + (right - left) // 2
Проверь себя
В задаче Search in Rotated Sorted Array [4, 5, 6, 7, 0, 1, 2], как определить, какая половина отсортирована?
В массиве [1, 2, 2, 2, 3, 4] при поиске первого вхождения числа 2, что делает алгоритм когда находит target?
Какое условие цикла используется в шаблоне поиска левой границы (first true) в бинарном поиске?
В задаче Koko Eating Bananas (piles = [3, 6, 7, 11], h = 8), какова минимальная скорость поедания бананов?
Почему для вычисления mid используют `$left + intdiv($right - $left, 2)` вместо `intdiv($left + $right, 2)`?