Первая причина в том, что std::find
указан в терминах линейного поиска. Между тем, std::set.find
указывается в терминах поиска логарифмического времени.
Но если вы замените std::find
на std::equal_range
, который будет выполнять бинарный поиск, вы обнаружите, что он медленный, как std::find
.
Так что я отвечу на вопрос лучше, чем вы:
Почему std::equal_range
смехотворно медленен на итераторах множества?
Ну, на самом деле нет веской причины.
std::set
итераторы - это двунаправленные итераторы. Это означает, что они позволяют идти вперед на один шаг или назад на один шаг.
std::equal_range
на двунаправленных итераторах чрезвычайно медленный , потому что он должен шаг за шагом проходить через диапазон.
Метод std::set.find
, с другой стороны, использует древовидную структуру std::set
, чтобы быстро найти элемент. По сути, он может очень быстро получить середины диапазона.
C ++ не раскрывает эту древовидную структуру при доступе к std::set
через его итераторы. Если бы это было так, то могла бы существовать такая операция, как std::somewhere_between( start, finish )
, которая за O (1) время получала бы итератор между start
и finish
, возвращая finish
, если такого итератора не существует.
Такая операция на самом деле очень дешева при реализации древовидной структуры std::set
.
Однако эта операция не существует. Так что std::equal_range( begin(set), end(set) )
смехотворно медленный.
Возможно, отсутствие такой операции, как std::somewhere_between
для отсортированных ассоциативных контейнеров, делает некоторые реализации set / map более эффективными; многие использовали специальные узлы для замены некоторых конечных случаев. И, возможно, вам потребуется доступ к этому специальному узлу для эффективного бинарного поиска в дереве.
Но я серьезно сомневаюсь, что эта операция не стоит того. С помощью этой операции вы можете эффективно работать в подразделе std::set
или std::map
; без этого у тебя ничего нет.