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


158

Що я маю на увазі, це те, що ми знаємо, що std::mapелементи впорядковані за клавішами. Отже, скажімо, що ключі - цілі числа. Якщо я перейду std::map::begin()до std::map::end()використання a 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був би кращий вибір, але зауважте мої "дуже рідкісні ситуації".


EDIT : Я знаю, що елементи std::mapсортовані .. немає необхідності вказувати на це (для більшості відповідей тут). Я навіть написав це у своєму запитанні.
Я запитував про ітератори та порядок, коли я переглядаю контейнер. Дякую @Kerrek SB за відповідь.


6
Якщо ви не знали: у реальному житті ви можете скористатися, map::upper_boundщоб знайти крапку, щоб почати ітерацію.
Стів Джессоп

Я це знаю, і я знаю саме те місце, де я б почав ітерацію. Я просто бродив, якщо замовлення гарантовано.
Кирило Кіров

Рідкий вектор не буде чутливим, якщо ваші клавіші (числові індекси) сильно різняться по всій дошці. Я використовую аналогічне рішення, для якого числовий індекс представляє декартову y-координату в тривимірному просторі. Використання вектора в цьому сценарії збільшило б мій слід пам’яті на гігабайти. Тому я не думаю, що вектор тут панацея, далеко від неї.
Джонатан Нойфельд

Я не розумію питання, і я поясню, чому через продуманий експеримент. Якщо ви вже знаєте, що елементи впорядковані, то як може бути ітерація? Що означає навіть замовлення, якщо воно не стосується ітерації? Які ще випадки існують, коли наказ має значення, виявляється тощо? (Відповідь дав Костянтин .)
підкреслюй_

Відповіді:


176

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

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


std :: map реалізовано за допомогою бінарних дерев, тому технічно не виконується бінарний пошук.
jupp0r

11
@ jupp0r: Структурування діапазону як двійкового дерева пошуку - це особливий спосіб реалізувати бінарний пошук через діапазон. "Бінарний пошук" - це абстрактне поняття, а не конкретна реалізація. Не має значення, переходите ви через зсуви в масив чи стежите за вузлами посилань; це лише конкретні способи "розбиття дальності".
Керрек СБ

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. Так що це гарантовано.

Мені хотілося б переглядати всі елементи з ключовим значенням, що перевищує конкретне ціле значення.

Це, безумовно, можливо.


3

Так ... елементи в а std::map мають суворий слабкий порядок, тобто елементи будуть складені з набору (тобто не буде повторів ключів, які є "рівними"), а рівність визначається тестуванням на будь-якому два клавіші A і B, якщо клавіша A не менше ключа B, а B не менше A, то ключ A дорівнює клавіші B.

Незважаючи на це, ви не можете правильно сортувати елементи а, std::mapякщо слабке впорядкування для цього типу неоднозначне (у вашому випадку, коли ви використовуєте цілі числа як тип ключа, це не є проблемою). Ви повинні бути в змозі визначити операцію, яка визначає загальний порядок для типу, який ви використовуєте для ключів у вашому std::map, інакше у вас буде лише частковий порядок для ваших елементів або набору, який має властивість, де A може бути не порівнянним з B. Зазвичай у цьому сценарії відбудеться те, що ви зможете вставити пари ключ / значення, але ви можете отримати дублікати пар ключів / значень, якщо повторити всю карту та / або виявити "відсутні" пари ключ / значення при спробі виконання std::map::find()певної пари ключ / значення на карті.


Як і інші відповіді, це насправді не відповідає на моє запитання, все одно дякую.
Кирило Кіров

-3

start () може дати найменший елемент. Але це залежало від реалізації. Чи вказано це в стандарті C ++? Якщо ні, то небезпечно робити це припущення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.