Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

Совет 44. Используйте функции контейнеров вместо одноименных алгоритмов

Некоторые контейнеры содержат функции, имена которых совпадают с именами алгоритмов STL. Так, в ассоциативных контейнерах существуют функции

count
,
find
,
lower_bound
,
upper_bound
и
equal_range
, а в контейнере
list
предусмотрены функции
remove
,
remove_if
,
unique
,
sort
,
merge
и
reverse
. Как правило, эти функции используются вместо одноименных алгоритмов, что объясняется двумя причинами. Во-первых, функции
классов быстрее работают. Во-вторых, они лучше интегрированы с контейнерами (особенно ассоциативными), чем алгоритмы. Дело в том, что алгоритмы и одноименные функции классов обычно работают не совсем одинаково.

Начнем с ассоциативных контейнеров. Допустим, имеется множество

set<int>
, содержащее миллион значений, и вы хотите найти позицию первого вхождения числа 727, если оно присутствует. Ниже приведены два очевидных способа поиска:

set<int> s; // Создать множество

… // и занести в него

// миллион чисел

set<int>::iterator i = s.find(727); // Функция find контейнера

 if (i != s.end) …

 set<int>::iterator i = find(s.begin, s.end, 727); // Алгоритм find

 if (i != s.end) …

Функция класса

find
работает с логарифмической сложностью, поэтому независимо от того, присутствует ли число 727 в множестве или нет,
set::find
в процессе поиска выполнит не более 40 сравнений, а обычно потребуется не более 20. С другой стороны, алгоритм
find
работает с линейной сложностью, поэтому при отсутствии числа 727 будет выполнено 1 000 000 сравнений. Впрочем, даже если число 727 присутствует, алгоритм
find
в процессе поиска выполняет в среднем 500 000 сравнений. Результат явно не в пользу алгоритма
find
.

Кстати, я не совсем точно указал количество сравнений для функции

find
, поскольку оно зависит от реализации, используемой ассоциативными контейнерами. В большинстве реализаций используются красно-черные деревья — особая разновидность сбалансированных деревьев с разбалансировкой по степеням 2. В таких реализациях максимальное количество сравнений, необходимых для поиска среди миллиона значений, равно 38, но в подавляющем большинстве случаев требуется не более 22 сравнений. Реализация, основанная на идеально сбалансированных деревьях, никогда не требует более 21 сравнения, но на практике по общему быстродействию идеально сбалансированные деревья уступают «красно-черным». По этой причине в большинстве реализаций STL используются «красно-черные» деревья.

Различия между функцией класса и алгоритмом find не ограничиваются быстродействием. Как объясняется в совете 19, алгоритмы STL проверяют «одинаковость» двух объектов по критерию равенства, а ассоциативные контейнеры используют критерий эквивалентности. Таким образом, алгоритм

find
ищет 727 по критерию равенства, а функция
find
— по критерию эквивалентности. Различия в критериях иногда приводят к изменению результата поиска. Например, в совете 19 было показано, как применение алгоритма
find
для поиска информации в ассоциативном контейнере завершается неудачей, тогда как аналогичный поиск функцией
find
привел бы к успеху! При работе с ассоциативными контейнерами функциональные формы
find
,
count
и т. д. предпочтительнее алгоритмических, поскольку их поведение лучше согласуется с другими функциями этих контейнеров. Вследствие различий между равенством и эквивалентностью алгоритмы не столь последовательны.

Особенно ярко это различие проявляется при работе с контейнерами

map
и
multimap
, потому что эти контейнеры содержат объекты
pair
, но их функции учитывают только значение ключа каждой пары. По этой причине функция
count
считает только пары с совпадающими ключами (естественно, «совпадение» определяется по критерию эквивалентности); значение, ассоциированное с ключом, игнорируется. Функции
find
,
lower_bound
и т. д. поступают аналогично. Чтобы алгоритмы также ограничивались анализом ключа в каждой паре, вам придется выполнять акробатические трюки, описанные в совете 23 (что позволит заменить проверку равенства проверкой эквивалентности).

С другой стороны, если вы стремитесь к максимальной эффективности, то фокусы совета 23 в сочетании с логарифмической сложностью поиска алгоритмов из совета 34 могут показаться не такой уж высокой ценой за повышение быстродействия. А если вы очень сильно стремитесь к максимальной эффективности, подумайте об использовании нестандартных хэшированных контейнеров (см. совет 25), хотя в этом случае вы также столкнетесь с различиями между равенством и эквивалентностью.

Таким образом, для стандартных ассоциативных контейнеров применение функций вместо одноименных алгоритмов обладает сразу несколькими преимуществами. Во-первых, вы получаете логарифмическую сложность вместо линейной. Во-вторых, «одинаковость» двух величин проверяется по критерию эквивалентности, более естественному для ассоциативных контейнеров. В-третьих, при работе с контейнерами 

map
и
multimap
автоматически учитываются только значения ключей вместо полных пар (ключ, значение). Эти три фактора достаточно убедительно говорят в пользу функций классов.

Перейдем к функциям контейнера

list
, имена которых совпадают с именами алгоритмов STL. В этом случае эффективность является практически единственным фактором. Алгоритмы, у которых в контейнере
list
существуют специализированные версии (
remove
,
remove_if
,
unique
,
sort
,
merge
и
reverse
), копируют объекты, a
list
– версии ничего не копируют; они просто манипулируют указателями, соединяющими узлы списка. По алгоритмической сложности функции классов и алгоритмы одинаковы, но если предположить, что операции с указателями обходятся значительно дешевле копирования объектов,
list
– версии обладают лучшим быстродействием.

Следует помнить, что

list
– версии часто ведут себя иначе, чем их аналоги-алгоритмы. Как объясняется в совете 32, для фактического удаления элементов из контейнера вызовы алгоритмов
remove
,
remove_if
и
unique
должны сопровождаться вызовами
erase
, однако одноименные функции контейнера
list
честно уничтожают элементы, и последующие вызовы
erase
не нужны.

Принципиальное различие между алгоритмом

sort
и функцией
sort
контейнера
list
заключается в том, что алгоритм неприменим к контейнерам
list
, поскольку ему не могут передаваться двусторонние итераторы
list
. Алгоритм
merge
также отличается от функции
merge
контейнера
list
— алгоритму не разрешено модифицировать исходные интервалы, тогда как функция
list::merge
всегда модифицирует списки, с которыми она работает.

Теперь вы располагаете всей необходимой информацией. Столкнувшись с выбором между алгоритмом STL и одноименной функцией контейнера, предпочтение следует отдавать функции контейнера. Она почти всегда эффективнее работает и лучше интегрируется с обычным поведением контейнеров.

Совет 45. Различайте алгоритмы count, find, binary_search, lower_bound, upper_bound и equal_range

Предположим, вы ищете некоторый объект в контейнере или в интервале, границы которого обозначены итераторами. Как это сделать? В вашем распоряжении целый арсенал алгоритмов:

count
,
find
,
binary_search
,
lower_bound
,
upper_bound
и
equal_range
. Как же принять верное решение?

Поделиться:
Популярные книги

Моя на одну ночь

Тоцка Тала
Любовные романы:
современные любовные романы
короткие любовные романы
5.50
рейтинг книги
Моя на одну ночь

Черный Маг Императора 8

Герда Александр
8. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 8

Измена. Отбор для предателя

Лаврова Алиса
1. Отбор для предателя
Фантастика:
фэнтези
5.00
рейтинг книги
Измена. Отбор для предателя

Кодекс Крови. Книга II

Борзых М.
2. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга II

Шаг в бездну

Муравьёв Константин Николаевич
3. Перешагнуть пропасть
Фантастика:
фэнтези
космическая фантастика
7.89
рейтинг книги
Шаг в бездну

Часовая битва

Щерба Наталья Васильевна
6. Часодеи
Детские:
детская фантастика
9.38
рейтинг книги
Часовая битва

Вечная Война. Книга II

Винокуров Юрий
2. Вечная война.
Фантастика:
юмористическая фантастика
космическая фантастика
8.37
рейтинг книги
Вечная Война. Книга II

Хроники странного королевства. Вторжение. (Дилогия)

Панкеева Оксана Петровна
110. В одном томе
Фантастика:
фэнтези
9.38
рейтинг книги
Хроники странного королевства. Вторжение. (Дилогия)

Часовой ключ

Щерба Наталья Васильевна
1. Часодеи
Фантастика:
фэнтези
9.36
рейтинг книги
Часовой ключ

Инвестиго, из медика в маги

Рэд Илья
1. Инвестиго
Фантастика:
фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Инвестиго, из медика в маги

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

Очешуеть! Я - жена дракона?!

Амеличева Елена
Фантастика:
юмористическая фантастика
5.43
рейтинг книги
Очешуеть! Я - жена дракона?!

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9