Find two bugs in the following binary search implementation:
func search(arr []int, target int) int {
l := 0
r := len(arr) - 1
for l < r {
m := (l + r) / 2
if target < arr[m] {
r = m - 1
} else {
l = m
}
}
if arr[l] == target { //assume len(arr) != 0
return l
}
return -1
}None of this is Golang specific. If you know Java, C or
(
Read more... )
1. На массиве длиной в 0 сходу будет access violation in line "if arr[l] == target"
2. На практике маловероятно, но, в принципе, в "m := (l + r) / 2" может быть int overflow. По уму надо делать что-то вроде m := l/2 + r/2 + (l%2 + r%2)/2
3. "r = m - 1" может привести к "r < 0", когда число не нашлось, и у нас снова будет access violation in line "if arr[l] == target"
Хороший паттерн (не супер оптимальный по числу сравнений, зато безопасный) - это внутри цикла сделать тройной if, в котором будет проверка на равенство и early return, а за циклом unconditionally return -1.
Второй вариант - в условие после цикла добавить проверку границ: "if (l <= r) && (arr[l] == target)" (можно написать "if (r >= 0) && (arr[l] == target)", что чуть более эффективно, но это будет менее читаемо, так что я бы не стал ( ... )
Reply
Reply
2 - верно описана проблема/баг. Предложенная "альтернативная формула" мне с первого взгляда кажется неверной.
3 - мне кажется, что это не проблема. Отрицательное r вызовет завершение цикла. А ниже мы смотрим только на l. Извини (посмотрел твой update): мы, кажется, одинаково понимаем этот пункт.
Про всё остальное. Спасибо за подробный разбор! Я вечером "врублюсь" и отпишу по всем пунктам. Пока что это так "на бегу".
Reply
Фак! Увидел. Конечно, вместо "l = m" надо "l = m + 1". Лень придумывать пример, на котором получится вечный цикл, но уверен, что его можно построить.
Вернее, не так: надо и "l = m + 1", и отдельную проверку на точное равенство.
Без трёхстороннего if-а не обойтись.
Но я бы не допустил этот баг, т.к. всегда херачу трёхсторонний if в имплементации binary search.
Reply
Я бы, правда, чинил этот баг немного по-другому, но это уже скорее дело вкуса.
Твой способ мне тоже нравится! Он явно пуленепробиваемый. На собеседовании это важнее, чем вкусовщина.
Reply
Reply
Reply
Reply
Особенно зашло предложение не включать правый конец. Я вырос на C++. Там в STL везде так сделано.
Reply
Leave a comment