Курс по структурам данных: https://stepik.org/a/134212
На предыдущих занятиях мы с вами подробно
рассмотрели принципы работы хэш-таблиц. Естественно, возникает вопрос, как их
можно использовать в своих программах? Обычно, в каждом современном языке
программирования имеется класс (или классы), который позволяет создавать и
работать с хэш-таблицами. Мы затронем два популярных языка программирования: Python и C++. В Python на основе
хэш-таблиц реализован словарь dict и множество set. Как с ними
работать я уже подробно рассказывал в курсе «Python для начинающих»:
Поэтому здесь лишь затрону их в целом.
Хэш-таблицы в Python
Когда мы в программе на Python создаем словарь,
например:
d = {'one': 1, 'two': 2, 'five': 5}
то формируется
хэш-таблица с ключами 'one', 'two', 'five' и соответствующими
значениями 1, 2, 5. Если нам нужно добавить в эту хэш-таблицу еще какой-либо
ключ, то это можно сделать так:
А считывание
значений по ключу выполняется, например, командой:
value = d['one']
print(value)
Как видите, все
предельно просто и при этом мы имеем возможность представлять данные в формате
«ключ-значение» на уровне хэш-таблицы. Кстати, именно поэтому в качестве ключей
словаря должны использоваться только хэшируемые объекты. В Python к ним относятся
все неизменяемые типы данных, такие как строки, числа, булевы значения, кортежи
и т.п. А вот если попытаться указать нехэшируемые (изменяемые) объекты, скажем,
список:
то в процессе
выполнения этой команды будет сгенерирована ошибка:
TypeError:
unhashable type: 'list'
Что означает,
что список (list) относится к
нехэшируемым данным и не может использоваться в качестве ключа хэш-таблицы.
По аналогии со
словарями в Python работает и
множество:
s = {'one', 'two', 'five'}
Синтаксис очень
похож, только здесь как бы фигурируют только ключи (значения множества), а
значений у ключей никаких нет. Все эти ключи также сохраняются в хэш-таблице,
образуя значения множества. Отсюда сразу автоматически следует, что значениями
множества могут выступать только хэшируемые объекты, например:
s = {'one', (1, 2, 3), True, 10, 5.8}
А вот
нехэшируемые приводят к той же ошибке, что и у словарей:
s = {'one', (1, 2, 3), True, 10, 5.8, [1, 2, 3]}
Здесь последний
элемент список (list) относится к изменяемому, а значит, нехэшируемому
типу.
Хэш-таблицы в С++
В языке С++ для
использования хэш-таблиц можно воспользоваться классом unordered_map
стандартной библиотеки шаблонов STL. Для этого вначале подключается
заголовок:
и в функции main() получаем
возможность создавать объекты (хэш-таблицы) с помощью этого класса:
int main()
{
setlocale(LC_ALL, "ru");
using namespace std;
unordered_map<string, int> ar;
return 0;
}
Класс unordered_map в целом имеет
тот же функционал (тот же набор методов), что и класс map, о котором мы
уже с вами говорили. Только map реализован на основе красно-черного
бинарного дерева, а unordered_map – на основе
хэш-таблиц. Поэтому в программе выбирается тот класс, который предпочтительнее
использовать для хранения и обработки данных.
Так как
функционал этих классов схожий, то я кратко продемонстрирую работу с unordered_map. При создании
объекта, мы в угловых скобках указываем тип ключа и тип значения. В примере
ключ имеет тип string (строка), а значение тип int (целое число). Сам
объект ar представляет
собой пустую хэш-таблицу.
Чтобы внести в
нее какие-либо данные, можно воспользоваться или методом insert():
ar.insert(pair<string, int>("one", 1));
ar.insert(make_pair("three", 3));
но проще и лучше
методом emplace():
Для перебора
содержимого хэш-таблицы удобно использовать цикл for следующим
образом:
for (auto& item : ar)
{
cout << item.first << " " << item.second << endl;
}
В результате
увидим на экране строчки:
one 1
three 3
four
4
Также добавлять
новые данные можно с помощью оператора квадратные скобки:
И читать
значения по ключу:
auto val = ar["one"];
cout << val << endl;
Для удаления
ключа из хэш-таблицы можно воспользоваться методом erase():
auto it = ar.erase("three");
Данный метод
возвращает булево значение false, если удаление ключа по каким-либо
причинам не было выполнено и true в противном случае.
Для поиска
элемента по ключу используется метод find():
auto it = ar.find("two");
Данный метод
возвращает итератор на пару ключ-значение, если указанный ключ был найден, либо
значение ar.end(), если ключ не
найден.
Класс unordered_set
Наряду с классом
unordered_map в библиотеке STL языка С++ есть еще один подобный класс
unordered_set, который
реализует множество на основе хэш-таблиц. Функционал этого класса подобен функционалу
класса set, о котором мы с
вами уже говорили.
Чтобы
использовать unordered_set в своей программе, нужно вначале
подключить заголовок:
А, затем, в
функции main() создать
экземпляр этого класса, например, так:
int main()
{
setlocale(LC_ALL, "ru");
using namespace std;
unordered_set<int> s = { 1, 2, 3 };
for (auto& item : s)
{
cout << item << " " << endl;
}
return 0;
}
Мы здесь сразу
инициализируем множество значениями 1, 2, 3. В результате создается хэш-таблица
с такими ключами и далее, с помощью цикла for, мы перебираем
эту хэш-таблицу и выводим значения ключей в консоль.
Я не буду
подробно останавливаться на работе с классом unordered_set, так как он
содержит подобный набор методов, что и класс set. Основные из
них приведены в таблице ниже.
Метод
|
Описание
|
Сложность
|
begin(), cbegin()
|
Возвращает
итератор на первый элемент списка
|
O(1)
|
end(), cend()
|
Возвращает
итератор на последний элемент списка
|
O(1)
|
size()
|
Возвращает
число элементов в списке
|
O(1)
|
insert()
|
Вставка
нового элемента в произвольную позицию
|
O(1)
|
erase()
|
Удаление
произвольного элемента списка
|
O(1)
|
find()
|
Поиск
элемента по значению
|
O(1)
|
clear()
|
Очистка
двусвязного списка
|
-
|
empty()
|
Возвращает
true, если
множество пустое и false в противном случае
|
O(1)
|
Например, мы
можем выполнить следующие операции:
cout << s.size() << endl;
s.insert(4);
s.erase(2);
auto it = s.find<int>(1);
if (it != s.end())
{
cout << *it << endl;
}
Главное здесь
знать, что unordered_set использует
хэш-таблицу для хранения и обработки значений, а класс set – бинарное
красно-черное дерево. Во всем остальном эти два класса очень похожи.
Заключение
На этом занятии
мы увидели с вами, как можно применять хэш-таблицы для хранения данных в языках
Python и С++. Причем,
хэш-таблицы могут быть использованы не только в формате «ключ-значение» (как
это у карт (map) и словарей (dict)), но и просто
в формате «ключ», как это сделано у множеств.
Курс по структурам данных: https://stepik.org/a/134212