Известен ли порядок итерации через std :: map (и гарантирован стандартом)?

158

Я имею в виду, что мы знаем, что std::mapэлементы отсортированы по ключам. Итак, допустим, что ключи являются целыми числами. Если я итерация от std::map::begin()с std::map::end()использованием for, делает стандартную гарантию того, что я буду перебирать , следовательно , через элементы с ключами, сортируются в порядке возрастания?


Пример:

std::map<int, int> map_;
map_[1] = 2;
map_[2] = 3;
map_[3] = 4;
for( std::map<int, int>::iterator iter = map_.begin();
     iter != map_.end();
     ++iter )
{
    std::cout << iter->second;
}

Это гарантированно печатать 234или это определяется реализацией?


Причина реальной жизни: у меня есть std::mapс intключами. В очень редких ситуациях я хотел бы пройтись по всем элементам, причем ключ больше конкретного intзначения. Да, это звучит как std::vectorлучший выбор, но обратите внимание на мои "очень редкие ситуации".


РЕДАКТИРОВАТЬ : Я знаю, что элементы std::mapотсортированы .. нет необходимости указывать на это (для большинства ответов здесь). Я даже написал это в своем вопросе.
Я спрашивал об итераторах и порядке, когда я перебираю контейнер. Спасибо @Kerrek SB за ответ.

Кирилл Киров
источник
6
Если вы не знали: в своей реальной жизни вы можете использовать, map::upper_boundчтобы найти точку для начала итерации.
Стив Джессоп
Я знаю это, и я знаю точное место, которое я бы начал повторять. Я просто забрел, если заказ гарантирован.
Кирилл Киров
Разреженный вектор не будет иметь смысла, если ваши ключи (числовые индексы) сильно различаются по всем направлениям. Я использую аналогичное решение, для которого числовой индекс представляет собой декартову Y-координату в трехмерном пространстве. Использование вектора в этом сценарии увеличило бы объем памяти на гигабайты. Так что я не думаю, что вектор - это панацея, это далеко не так.
Джонатан Нойфельд
Я не понимаю вопроса, и я объясню почему с помощью мысленного эксперимента. Если вы уже знаете, что элементы упорядочены, как может быть итерация? Что означает порядок, если он не применяется к итерации? В каких других случаях порядок имеет значение, обнаруживается и т. Д.? (Ответ был дан Константином .)
underscore_d

Ответы:

176

Да, это гарантировано. Кроме того, *begin()дает самый маленький и *rbegin()самый большой элемент, как определено оператором сравнения, а также два ключевых значений aи bдля которых выражение !compare(a,b) && !compare(b,a)истинно считаются равными. Функция сравнения по умолчанию std::less<K>.

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

Керрек С.Б.
источник
std :: map реализован с использованием двоичных деревьев, поэтому технически никакой двоичный поиск не выполняется.
jupp0r
11
@ jupp0r: Структурирование диапазона в виде бинарного дерева поиска - это особый метод для реализации бинарного поиска по диапазону. «Бинарный поиск» - это абстрактное понятие, а не конкретная реализация. Неважно, прыгаете ли вы через смещения в массив или переходите по узлам связи; это всего лишь конкретные способы «деления пополам».
Kerrek SB
1
Я знаю, что это старый пост, но, чтобы быть ясным, «эффективный поиск» относителен. Технически он std::unordered_mapимеет более эффективное время поиска O (1). Преимущество std::mapзаключается в упорядочивании ключей, а не в поиске.
Адам Джонстон
42

Это гарантировано ассоциативными требованиями к контейнерам в стандарте C ++. Например, см. 23.2.4 / 10 в C ++ 11:

Фундаментальное свойство итераторов ассоциативных контейнеров состоит в том, что они
перебирать контейнеры в не убывающем порядке ключей, где
не убывание определяется сравнением, которое использовалось для их построения.
Для любых двух разыменованных итераторов i и j таких, что расстояние от i до j равно
положительны,
  value_comp (* j, * i) == false

и 23.2.4 / 11

Для ассоциативных контейнеров с уникальными ключами выполняется более сильное условие,
  value_comp (* i, * j)! = false.
Константин Ознобихин
источник
32

Я думаю, что в структурах данных есть путаница.

В большинстве языков a map- это просто AssociativeContainer: он сопоставляет ключ со значением. В «более новых» языках это обычно достигается с помощью хэш-карты, поэтому порядок не гарантируется.

Однако в C ++ это не так:

  • std::mapэто сортируется ассоциативный контейнер
  • std::unordered_map ассоциативный контейнер на основе хеш-таблицы, представленный в C ++ 11

Итак, для уточнения гарантий по оформлению заказа.

В C ++ 03:

  • std::set, std::multiset, std::mapИ std::multimapгарантированно будут упорядочены в соответствии с ключами (и критерий входит в комплект)
  • в std::multisetи std::multimapстандарт не накладывает никаких гарантий порядка на эквивалентные элементы (то есть те, которые сравнивают равные)

В C ++ 11:

  • std::set, std::multiset, std::mapИ std::multimapгарантированно будут упорядочены в соответствии с ключами (и критерий входит в комплект)
  • в std::multisetи std::multimapстандарт устанавливает, что эквивалентные элементы (те, которые сравниваются равными) упорядочены в соответствии с порядком их вставки (сначала вставляются в первую очередь)
  • std::unordered_*Контейнеры, как следует из названия, не заказаны. В частности, порядок элементов может изменяться при изменении контейнера (при вставке / удалении).

Когда в стандарте говорится, что элементы упорядочены таким образом, это означает, что:

  • при итерации вы видите элементы в определенном порядке
  • при обратной итерации вы видите элементы в обратном порядке

Я надеюсь, что это устранит любую путаницу.

Матье М.
источник
Это не имеет никакого отношения к моему вопросу, извините :) Я знаю, какой заказан, а какой - нет. И я прошу порядок, когда я перебираю элементы.
Кирилл Киров
10
@KirilKirov: ну, определение упорядоченного ассоциативного контейнера таково, что при итерации по нему элементы упорядочены.
Матье М.
Ну, я думаю, что вы правы, но я этого не знал, и это именно то, о чем я спрашивал :)
Кирилл Киров
4

Это гарантированно печатать 234 или его реализация определена?

Да, std::mapэто отсортированный контейнер, заказанный Keyс поставляемым Comparator. Так что это гарантировано.

Я хотел бы пройтись по всем элементам, причем ключ больше конкретного значения int.

Это, безусловно, возможно.

jpalecek
источник
3

Да ... элементы в a std::mapимеют строгий слабый порядок, что означает, что элементы будут составлены из набора (т. Е. Не будет повторений «равных» ключей), и равенство определяется путем тестирования на любом две клавиши A и B, что если ключ A не меньше ключа B, а B не меньше A, то ключ A равен ключу B.

При этом вы не можете правильно отсортировать элементы a, std::mapесли слабый порядок для этого типа неоднозначен (в вашем случае, когда вы используете целые числа в качестве типа ключа, это не проблема). Вы должны быть в состоянии определить операцию, которая определяет общий порядок для типа, который вы используете для ключей в вашем std::map, иначе у вас будет только частичный порядок для ваших элементов, или poset, у которого есть свойство, где A может быть несопоставимым с B. Что обычно происходит в этом сценарии, так это то, что вы сможете вставить пары ключ / значение, но у вас могут получиться дублирующие пары ключ / значение, если вы выполните итерацию по всей карте и / или обнаружите «пропущенные» пары ключ / значение при попытке выполнить std::map::find()определенную пару ключ / значение на карте.

Джейсон
источник
Как и другие ответы, это на самом деле не отвечает на мой вопрос, в любом случае, спасибо.
Кирилл Киров
-3

begin () может дать наименьший элемент. Но это зависит от реализации. Это указано в стандарте C ++? Если нет, то делать такое предположение опасно.

pyang
источник