1. Списки: Начало

☰ Теория

Обычные массивы в программировании обращаются к элементам по номерам: 0, 1, 2 и так далее. Это не всегда удобно. Например, если вы хотите хранить, сколько раз каждое слово встретилось в тексте, вам хочется искать по самому слову, а не по номеру. Для таких задач в C++ есть ассоциативные контейнеры — структуры данных, где каждому значению соответствует ключ (ассоциативные массивы).

Представьте себе словарь: ключ — это слово, значение — его определение. В программировании ключом может быть строка, число или даже сложный объект, а значением — что угодно.

В C++ чаще всего используют два контейнера такого типа: map и multimap. В map каждый ключ встречается только один раз, то есть одному ключу соответствует одно значение. В multimap допускаются повторяющиеся ключи: одному и тому же ключу могут соответствовать несколько значений, и контейнер хранит их все. Обе структуры устроены так, что операции поиска, вставки и удаления работают быстро — примерно за O(log⁡n), где n — количество элементов. Это означает “очень быстро”, даже если элементов много.
 

Контейнер map

Для использования map вам необходимо сначала его подключить:
#include <map>
 
Пример создания map, у которого ключом является строка, а значением целое число:
map<string, int> mymap;

Основные операторы

Функция Описание Пример
empty() Проверяет, пустой ли контейнер if (m.empty()) std::cout << "empty";
size() Количество элементов std::cout << m.size();
max_size() Теоретически максимальный размер auto mx = m.max_size();
count(key) Сколько элементов с ключом. В map: 0 или 1 if (m.count("one")) { /* есть */ }
find(key) Итератор на элемент с ключом или end() auto it = m.find("one"); if (it != m.end()) std::cout << it->second;
lower_bound(key) Первый элемент с ключом ≥ key auto it = m.lower_bound("b");
upper_bound(key) Первый элемент с ключом > key auto it = m.upper_bound("b");
equal_range(key) Пара итераторов [первый, после последнего] auto [l, r] = m.equal_range("b"); for (auto it=l; it!=r; ++it) {}
operator[] (m[key]) Доступ по ключу. Если нет — создаёт со значением по умолчанию m["one"]++;
at(key) Доступ без создания. Если нет — исключение int v = m.at("one");
insert(el) Вставка элемента (пары ключ-значение) m.insert({"cat", 3});
insert(beg, end) Вставка диапазона m.insert(v.begin(), v.end());
erase(key) Удалить по ключу, вернуть кол-во удалённых m.erase("cat");
erase(pos) Удалить по итератору m.erase(m.find("cat"));
erase(beg, end) Удалить диапазон m.erase(m.begin(), m.end());
clear() Удалить все элементы m.clear();
begin() / end() Итераторы на начало/конец for (auto it=m.begin(); it!=m.end(); ++it) {}
contains(key) (C++20) Быстрая проверка существования без вставки if (m.contains("one")) { /* ... */ }


Обратите внимание на count(): проверять наличие индекса таким образом нельзя:

if ( mymap["one"] == 0 )
это вызовет автоматическое создание в массиве элемента с ключом one.

Поэтому проверять надо так:

if( mymap.count("one") == 0 )
 

Подсказка:

  • Для счётчиков удобно использовать m[key]++.

  • Для “просто узнать, есть ли ключ” — count или contains (C++20).

  • Для безопасного чтения без создания — at.

 

Вывод содержимого map

Чтобы вывести все пары ключ–значение из std::map, можно проходить по контейнеру итератором. Итератор — это “указатель-стрелка” по контейнеру, который двигается от начала к концу.
 
// Обход с явным итератором:
map<string, int>::iterator it;
for (it = mymap.begin(); it != mymap.end(); ++it) {
    cout << it->first << " " << it->second << '\n';
}

где:
begin() - итератор на первый элемент,
end() - итератор на элемент идущий после последнего,
first - указатель на ключ
second  - указатель на значение

Тот же обход можно написать короче с помощью range-based for (C++11):

for (const auto& kv : mymap) {
    cout << kv.first << "\t" << kv.second << endl;
}


Если у вас C++17, ещё нагляднее использовать распаковку пары:

for (const auto& [key, value] : mymap) {
    cout << key << "\t" << value << endl;
}



 

Построить алфавитно-частотный словарь: список слов в алфавитном порядке, справа от каждого слова должно быть указано, сколько раз оно встречается в исходном файле. Признаком окончания текста является "END!". Порядок вывода слов роли не играет.
Примеры
Входные данныеВыходные данные
1 Съешь ещё этих мягких французских булок END!
Съешь 1
ещё 1
этих 1
мягких 1
французских 1
булок 1

Вставьте недостающие фрагменты кода
C++
#include<iostream>
#include<map>
#include<string>
using namespace std;


int main()
{

	map<string, int> mymap;
	string s;
	while (true)
	{                
	}

	map<string, int>::iterator it;

	for (it = mymap.begin(); it != mymap.end(); ++it)
		cout << it->first << " " << it->second << "\n";
	return 0;
}