← Все вопросы
lower_bound и upper_bound в C++: чем отличаются и какие частые грабли?
12
Постоянно путаю lower_bound и upper_bound. Как точно запомнить, что каждый возвращает? И почему lower_bound на std::set иногда работает за O(n), хотя я думал, что бинпоиск всегда O(log n)?
2 ответа
18
✓ Принятый ответ — помог автору
Для отсортированного диапазона:
lower_bound(first, last, x)→ итератор на первый элемент>= x(первая позиция, куда можно вставить x, не нарушив порядок, левее равных).upper_bound(first, last, x)→ итератор на первый элемент> x(вставка правее равных).
Отсюда полезные формулы:
- Число элементов, равных x:
upper_bound(...) - lower_bound(...). - Число элементов
< x:lower_bound(...) - begin. - Число
<= x:upper_bound(...) - begin.
vector<int> a = {1, 2, 2, 2, 5};
int lt2 = lower_bound(a.begin(), a.end(), 2) - a.begin(); // 1 (индекс первого 2)
int gt2 = upper_bound(a.begin(), a.end(), 2) - a.begin(); // 4
int cnt2 = gt2 - lt2; // 3 — сколько двоек
Сложность O(log n) для random-access итераторов (vector, array).
Грабля с set. Свободные функции std::lower_bound(s.begin(), s.end(), x) на set дают O(n)! Итераторы set — bidirectional, а не random-access, поэтому std::lower_bound делает O(log n) сравнений, но O(n) шагов итератора. Используй методы-члены: s.lower_bound(x) и s.upper_bound(x) — они O(log n) по дереву. То же для map. Это классический источник скрытого TLE.
7
Ещё две грабли:
- Перед
lower_bound/upper_boundмассив обязан быть отсортирован по тому же компаратору. Если сортировал по убыванию, передавай тот же компаратор третьим/четвёртым аргументом (greater<int>()), иначе UB и мусор. - Проверяй, что итератор не равен
end(), прежде чем разыменовывать:auto it = lower_bound(...); if (it != a.end() && *it == x) .... Очень частый RE/WA — обращение к*it, когда x больше всех элементов иit == end().
Ваш ответ
Войдите, чтобы ответить на вопрос.