6.1 Хеш-таблица¶
Хеш-таблица (hash table), также называемая таблицей рассеяния, обеспечивает эффективный поиск элементов за счет отображения между ключом key и значением value . Иначе говоря, если передать в хеш-таблицу ключ key , то можно за \(O(1)\) времени получить соответствующее значение value .
Как показано на рисунке 6-1, пусть есть \(n\) студентов, и у каждого из них есть два поля данных: "имя" и "номер студенческого билета". Если мы хотим реализовать запрос вида "ввести номер студенческого билета и вернуть соответствующее имя", то для этого можно использовать показанную ниже хеш-таблицу.

Рисунок 6-1 Абстрактное представление хеш-таблицы
Помимо хеш-таблицы, функции поиска можно реализовать и через массив, и через связный список. Сравнение их эффективности приведено в таблице 6-1.
- Добавление элемента: нужно лишь добавить элемент в конец массива (или списка), что занимает \(O(1)\) времени.
- Поиск элемента: так как массив (или список) неупорядочен, приходится обходить все элементы, что занимает \(O(n)\) времени.
- Удаление элемента: сначала нужно найти элемент, затем удалить его из массива (или списка), что занимает \(O(n)\) времени.
Таблица 6-1 Сравнение эффективности поиска элементов
| Массив | Связный список | Хеш-таблица | |
|---|---|---|---|
| Поиск элемента | \(O(n)\) | \(O(n)\) | \(O(1)\) |
| Добавление элемента | \(O(1)\) | \(O(1)\) | \(O(1)\) |
| Удаление элемента | \(O(n)\) | \(O(n)\) | \(O(1)\) |
Нетрудно заметить, что операции чтения, добавления, удаления и обновления в хеш-таблице имеют временную сложность \(O(1)\) , то есть выполняются очень эффективно.
6.1.1 Основные операции с хеш-таблицей¶
К базовым операциям хеш-таблицы относятся инициализация, поиск, добавление пар ключ-значение и удаление пар ключ-значение. Пример кода приведен ниже:
# Инициализация хеш-таблицы
hmap: dict = {}
# Операция добавления
# Добавить пару ключ-значение (key, value) в хеш-таблицу
hmap[12836] = "Сяо Ха"
hmap[15937] = "Сяо Ло"
hmap[16750] = "Сяо Суань"
hmap[13276] = "Сяо Фа"
hmap[10583] = "Сяо Я"
# Операция поиска
# Передать в хеш-таблицу ключ key и получить значение value
name: str = hmap[15937]
# Операция удаления
# Удалить пару ключ-значение (key, value) из хеш-таблицы
hmap.pop(10583)
/* Инициализация хеш-таблицы */
unordered_map<int, string> map;
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map[12836] = "Сяо Ха";
map[15937] = "Сяо Ло";
map[16750] = "Сяо Суань";
map[13276] = "Сяо Фа";
map[10583] = "Сяо Я";
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
string name = map[15937];
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.erase(10583);
/* Инициализация хеш-таблицы */
Map<Integer, String> map = new HashMap<>();
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map.put(12836, "Сяо Ха");
map.put(15937, "Сяо Ло");
map.put(16750, "Сяо Суань");
map.put(13276, "Сяо Фа");
map.put(10583, "Сяо Я");
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
String name = map.get(15937);
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.remove(10583);
/* Инициализация хеш-таблицы */
Dictionary<int, string> map = new() {
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
{ 12836, "Сяо Ха" },
{ 15937, "Сяо Ло" },
{ 16750, "Сяо Суань" },
{ 13276, "Сяо Фа" },
{ 10583, "Сяо Я" }
};
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
string name = map[15937];
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.Remove(10583);
/* Инициализация хеш-таблицы */
hmap := make(map[int]string)
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
hmap[12836] = "Сяо Ха"
hmap[15937] = "Сяо Ло"
hmap[16750] = "Сяо Суань"
hmap[13276] = "Сяо Фа"
hmap[10583] = "Сяо Я"
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
name := hmap[15937]
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
delete(hmap, 10583)
/* Инициализация хеш-таблицы */
var map: [Int: String] = [:]
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map[12836] = "Сяо Ха"
map[15937] = "Сяо Ло"
map[16750] = "Сяо Суань"
map[13276] = "Сяо Фа"
map[10583] = "Сяо Я"
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
let name = map[15937]!
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.removeValue(forKey: 10583)
/* Инициализация хеш-таблицы */
const map = new Map();
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map.set(12836, 'Сяо Ха');
map.set(15937, 'Сяо Ло');
map.set(16750, 'Сяо Суань');
map.set(13276, 'Сяо Фа');
map.set(10583, 'Сяо Я');
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
let name = map.get(15937);
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.delete(10583);
/* Инициализация хеш-таблицы */
const map = new Map<number, string>();
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map.set(12836, 'Сяо Ха');
map.set(15937, 'Сяо Ло');
map.set(16750, 'Сяо Суань');
map.set(13276, 'Сяо Фа');
map.set(10583, 'Сяо Я');
console.info('\nПосле добавления хеш-таблица имеет вид\nKey -> Value');
console.info(map);
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
let name = map.get(15937);
console.info('\nПо номеру 15937 найдено имя ' + name);
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.delete(10583);
console.info('\nПосле удаления 10583 хеш-таблица имеет вид\nKey -> Value');
console.info(map);
/* Инициализация хеш-таблицы */
Map<int, String> map = {};
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map[12836] = "Сяо Ха";
map[15937] = "Сяо Ло";
map[16750] = "Сяо Суань";
map[13276] = "Сяо Фа";
map[10583] = "Сяо Я";
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
String name = map[15937];
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.remove(10583);
use std::collections::HashMap;
/* Инициализация хеш-таблицы */
let mut map: HashMap<i32, String> = HashMap::new();
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map.insert(12836, "Сяо Ха".to_string());
map.insert(15937, "Сяо Ло".to_string());
map.insert(16750, "Сяо Суань".to_string());
map.insert(13279, "Сяо Фа".to_string());
map.insert(10583, "Сяо Я".to_string());
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
let _name: Option<&String> = map.get(&15937);
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
let _removed_value: Option<String> = map.remove(&10583);
/* Инициализация хеш-таблицы */
val map = HashMap<Int,String>()
/* Операция добавления */
// Добавить пару ключ-значение (key, value) в хеш-таблицу
map[12836] = "Сяо Ха"
map[15937] = "Сяо Ло"
map[16750] = "Сяо Суань"
map[13276] = "Сяо Фа"
map[10583] = "Сяо Я"
/* Операция поиска */
// Передать в хеш-таблицу ключ key и получить значение value
val name = map[15937]
/* Операция удаления */
// Удалить пару ключ-значение (key, value) из хеш-таблицы
map.remove(10583)
# Инициализация хеш-таблицы
hmap = {}
# Операция добавления
# Добавить пару ключ-значение (key, value) в хеш-таблицу
hmap[12836] = "Сяо Ха"
hmap[15937] = "Сяо Ло"
hmap[16750] = "Сяо Суань"
hmap[13276] = "Сяо Фа"
hmap[10583] = "Сяо Я"
# Операция поиска
# Передать в хеш-таблицу ключ key и получить значение value
name = hmap[15937]
# Операция удаления
# Удалить пару ключ-значение (key, value) из хеш-таблицы
hmap.delete(10583)
Визуализация выполнения
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F%20%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20%23%20%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B2%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%20%D0%BF%D0%B0%D1%80%D1%83%20%D0%BA%D0%BB%D1%8E%D1%87-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%28key%2C%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%A5%D0%B0%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%9B%D0%BE%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%A1%D1%83%D0%B0%D0%BD%D1%8C%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%A4%D0%B0%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%D0%A3%D1%82%D0%B5%D0%BD%D0%BE%D0%BA%22%0A%20%20%20%20%0A%20%20%20%20%23%20%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F%20%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0%0A%20%20%20%20%23%20%D0%9F%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%82%D1%8C%20%D0%BA%D0%BB%D1%8E%D1%87%20key%20%D0%B2%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%20%D0%B8%20%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C%20%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20value%0A%20%20%20%20name%20%3D%20hmap%5B15937%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F%20%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20%23%20%D0%A3%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C%20%D0%B8%D0%B7%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B%20%D0%BF%D0%B0%D1%80%D1%83%20%D0%BA%D0%BB%D1%8E%D1%87-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%28key%2C%20value%29%0A%20%20%20%20hmap.pop%2810583%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
У хеш-таблицы есть три распространенных способа обхода: обход пар ключ-значение, обход ключей и обход значений. Примеры кода приведены ниже:
/* Обход хеш-таблицы */
// Обход пар ключ-значение key->value
for (Map.Entry <Integer, String> kv: map.entrySet()) {
System.out.println(kv.getKey() + " -> " + kv.getValue());
}
// Обход только ключей key
for (int key: map.keySet()) {
System.out.println(key);
}
// Обход только значений value
for (String val: map.values()) {
System.out.println(val);
}
/* Обход хеш-таблицы */
// Обход пар ключ-значение Key->Value
foreach (var kv in map) {
Console.WriteLine(kv.Key + " -> " + kv.Value);
}
// Обход только ключей key
foreach (int key in map.Keys) {
Console.WriteLine(key);
}
// Обход только значений value
foreach (string val in map.Values) {
Console.WriteLine(val);
}
/* Обход хеш-таблицы */
console.info('\nОбход пар ключ-значение Key->Value');
for (const [k, v] of map.entries()) {
console.info(k + ' -> ' + v);
}
console.info('\nОбход только ключей Key');
for (const k of map.keys()) {
console.info(k);
}
console.info('\nОбход только значений Value');
for (const v of map.values()) {
console.info(v);
}
/* Обход хеш-таблицы */
console.info('\nОбход пар ключ-значение Key->Value');
for (const [k, v] of map.entries()) {
console.info(k + ' -> ' + v);
}
console.info('\nОбход только ключей Key');
for (const k of map.keys()) {
console.info(k);
}
console.info('\nОбход только значений Value');
for (const v of map.values()) {
console.info(v);
}
Визуализация выполнения
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F%20%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20%23%20%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B2%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%20%D0%BF%D0%B0%D1%80%D1%83%20%D0%BA%D0%BB%D1%8E%D1%87-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%28key%2C%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%A5%D0%B0%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%9B%D0%BE%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%A1%D1%83%D0%B0%D0%BD%D1%8C%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%D0%A1%D1%8F%D0%BE%20%D0%A4%D0%B0%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%D0%A3%D1%82%D0%B5%D0%BD%D0%BE%D0%BA%22%0A%20%20%20%20%0A%20%20%20%20%23%20%D0%9F%D0%B5%D1%80%D0%B5%D0%B1%D1%80%D0%B0%D1%82%D1%8C%20%D1%85%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%0A%20%20%20%20%23%20%D0%9E%D0%B1%D0%BE%D0%B9%D1%82%D0%B8%D0%BF%D0%B0%D1%80%D0%B0%20%D0%BA%D0%BB%D1%8E%D1%87-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20key-%3Evalue%0A%20%20%20%20for%20key%2C%20value%20in%20hmap.items%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%2C%20%22-%3E%22%2C%20value%29%0A%20%20%20%20%23%20%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%9E%D0%B1%D0%BE%D0%B9%D1%82%D0%B8%D0%BA%D0%BB%D1%8E%D1%87%20key%0A%20%20%20%20for%20key%20in%20hmap.keys%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%29%0A%20%20%20%20%23%20%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%9E%D0%B1%D0%BE%D0%B9%D1%82%D0%B8%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20value%0A%20%20%20%20for%20value%20in%20hmap.values%28%29%3A%0A%20%20%20%20%20%20%20%20print%28value%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
6.1.2 Простая реализация хеш-таблицы¶
Сначала рассмотрим самый простой случай: реализуем хеш-таблицу только с помощью одного массива. В хеш-таблице каждую пустую ячейку массива мы называем бакетом (bucket), и каждый бакет может хранить одну пару ключ-значение. Следовательно, операция поиска сводится к тому, чтобы найти бакет, соответствующий key , и получить из него value .
Но как определить бакет, соответствующий заданному key ? Это делается с помощью хеш-функции (hash function). Назначение хеш-функции - отображать большое входное пространство в меньшее выходное пространство. В хеш-таблице входным пространством являются все key , а выходным - все бакеты (индексы массива). Иначе говоря, передав key на вход, мы можем через хеш-функцию получить позицию хранения соответствующей пары ключ-значение в массиве.
Процесс вычисления хеш-функции для одного key включает два шага.
- Сначала с помощью некоторого хеш-алгоритма
hash()вычисляется хеш-значение. - Затем хеш-значение берется по модулю числа бакетов (длины массива)
capacity, чтобы получить бакет (индекс массива)index, соответствующий этомуkey.
После этого можно использовать index для доступа к соответствующему бакету в хеш-таблице и получения value .
Пусть длина массива capacity = 100 , а хеш-алгоритм hash(key) = key . Тогда легко получить хеш-функцию key % 100 . На рисунке 6-2 на примере key "номер студенческого билета" и value "имя" показан принцип работы хеш-функции.

Рисунок 6-2 Принцип работы хеш-функции
Ниже приведен код простой реализации хеш-таблицы. В нем мы инкапсулируем key и value в класс Pair , чтобы представить пару ключ-значение.
class Pair:
"""Пара ключ-значение"""
def __init__(self, key: int, val: str):
self.key = key
self.val = val
class ArrayHashMap:
"""Хеш-таблица на основе массива"""
def __init__(self):
"""Конструктор"""
# Инициализировать массив, содержащий 100 корзин
self.buckets: list[Pair | None] = [None] * 100
def hash_func(self, key: int) -> int:
"""Хеш-функция"""
index = key % 100
return index
def get(self, key: int) -> str | None:
"""Операция поиска"""
index: int = self.hash_func(key)
pair: Pair = self.buckets[index]
if pair is None:
return None
return pair.val
def put(self, key: int, val: str):
"""Операции добавления и обновления"""
pair = Pair(key, val)
index: int = self.hash_func(key)
self.buckets[index] = pair
def remove(self, key: int):
"""Операция удаления"""
index: int = self.hash_func(key)
# Присвоить None, что означает удаление
self.buckets[index] = None
def entry_set(self) -> list[Pair]:
"""Получить все пары ключ-значение"""
result: list[Pair] = []
for pair in self.buckets:
if pair is not None:
result.append(pair)
return result
def key_set(self) -> list[int]:
"""Получить все ключи"""
result = []
for pair in self.buckets:
if pair is not None:
result.append(pair.key)
return result
def value_set(self) -> list[str]:
"""Получить все значения"""
result = []
for pair in self.buckets:
if pair is not None:
result.append(pair.val)
return result
def print(self):
"""Вывести хеш-таблицу"""
for pair in self.buckets:
if pair is not None:
print(pair.key, "->", pair.val)
/* Пара ключ-значение */
struct Pair {
public:
int key;
string val;
Pair(int key, string val) {
this->key = key;
this->val = val;
}
};
/* Хеш-таблица на основе массива */
class ArrayHashMap {
private:
vector<Pair *> buckets;
public:
ArrayHashMap() {
// Инициализировать массив, содержащий 100 корзин
buckets = vector<Pair *>(100);
}
~ArrayHashMap() {
// Освободить память
for (const auto &bucket : buckets) {
delete bucket;
}
buckets.clear();
}
/* Хеш-функция */
int hashFunc(int key) {
int index = key % 100;
return index;
}
/* Операция поиска */
string get(int key) {
int index = hashFunc(key);
Pair *pair = buckets[index];
if (pair == nullptr)
return "";
return pair->val;
}
/* Операция добавления */
void put(int key, string val) {
Pair *pair = new Pair(key, val);
int index = hashFunc(key);
buckets[index] = pair;
}
/* Операция удаления */
void remove(int key) {
int index = hashFunc(key);
// Освободить память и присвоить nullptr
delete buckets[index];
buckets[index] = nullptr;
}
/* Получить все пары ключ-значение */
vector<Pair *> pairSet() {
vector<Pair *> pairSet;
for (Pair *pair : buckets) {
if (pair != nullptr) {
pairSet.push_back(pair);
}
}
return pairSet;
}
/* Получить все ключи */
vector<int> keySet() {
vector<int> keySet;
for (Pair *pair : buckets) {
if (pair != nullptr) {
keySet.push_back(pair->key);
}
}
return keySet;
}
/* Получить все значения */
vector<string> valueSet() {
vector<string> valueSet;
for (Pair *pair : buckets) {
if (pair != nullptr) {
valueSet.push_back(pair->val);
}
}
return valueSet;
}
/* Вывести хеш-таблицу */
void print() {
for (Pair *kv : pairSet()) {
cout << kv->key << " -> " << kv->val << endl;
}
}
};
/* Пара ключ-значение */
class Pair {
public int key;
public String val;
public Pair(int key, String val) {
this.key = key;
this.val = val;
}
}
/* Хеш-таблица на основе массива */
class ArrayHashMap {
private List<Pair> buckets;
public ArrayHashMap() {
// Инициализировать массив, содержащий 100 корзин
buckets = new ArrayList<>();
for (int i = 0; i < 100; i++) {
buckets.add(null);
}
}
/* Хеш-функция */
private int hashFunc(int key) {
int index = key % 100;
return index;
}
/* Операция поиска */
public String get(int key) {
int index = hashFunc(key);
Pair pair = buckets.get(index);
if (pair == null)
return null;
return pair.val;
}
/* Операция добавления */
public void put(int key, String val) {
Pair pair = new Pair(key, val);
int index = hashFunc(key);
buckets.set(index, pair);
}
/* Операция удаления */
public void remove(int key) {
int index = hashFunc(key);
// Присвоить null, что означает удаление
buckets.set(index, null);
}
/* Получить все пары ключ-значение */
public List<Pair> pairSet() {
List<Pair> pairSet = new ArrayList<>();
for (Pair pair : buckets) {
if (pair != null)
pairSet.add(pair);
}
return pairSet;
}
/* Получить все ключи */
public List<Integer> keySet() {
List<Integer> keySet = new ArrayList<>();
for (Pair pair : buckets) {
if (pair != null)
keySet.add(pair.key);
}
return keySet;
}
/* Получить все значения */
public List<String> valueSet() {
List<String> valueSet = new ArrayList<>();
for (Pair pair : buckets) {
if (pair != null)
valueSet.add(pair.val);
}
return valueSet;
}
/* Вывести хеш-таблицу */
public void print() {
for (Pair kv : pairSet()) {
System.out.println(kv.key + " -> " + kv.val);
}
}
}
/* Пара ключ-значение int->string */
class Pair(int key, string val) {
public int key = key;
public string val = val;
}
/* Хеш-таблица на основе массива */
class ArrayHashMap {
List<Pair?> buckets;
public ArrayHashMap() {
// Инициализировать массив, содержащий 100 корзин
buckets = [];
for (int i = 0; i < 100; i++) {
buckets.Add(null);
}
}
/* Хеш-функция */
int HashFunc(int key) {
int index = key % 100;
return index;
}
/* Операция поиска */
public string? Get(int key) {
int index = HashFunc(key);
Pair? pair = buckets[index];
if (pair == null) return null;
return pair.val;
}
/* Операция добавления */
public void Put(int key, string val) {
Pair pair = new(key, val);
int index = HashFunc(key);
buckets[index] = pair;
}
/* Операция удаления */
public void Remove(int key) {
int index = HashFunc(key);
// Присвоить null, что означает удаление
buckets[index] = null;
}
/* Получить все пары ключ-значение */
public List<Pair> PairSet() {
List<Pair> pairSet = [];
foreach (Pair? pair in buckets) {
if (pair != null)
pairSet.Add(pair);
}
return pairSet;
}
/* Получить все ключи */
public List<int> KeySet() {
List<int> keySet = [];
foreach (Pair? pair in buckets) {
if (pair != null)
keySet.Add(pair.key);
}
return keySet;
}
/* Получить все значения */
public List<string> ValueSet() {
List<string> valueSet = [];
foreach (Pair? pair in buckets) {
if (pair != null)
valueSet.Add(pair.val);
}
return valueSet;
}
/* Вывести хеш-таблицу */
public void Print() {
foreach (Pair kv in PairSet()) {
Console.WriteLine(kv.key + " -> " + kv.val);
}
}
}
/* Пара ключ-значение */
type pair struct {
key int
val string
}
/* Хеш-таблица на основе массива */
type arrayHashMap struct {
buckets []*pair
}
/* Инициализация хеш-таблицы */
func newArrayHashMap() *arrayHashMap {
// Инициализировать массив, содержащий 100 корзин
buckets := make([]*pair, 100)
return &arrayHashMap{buckets: buckets}
}
/* Хеш-функция */
func (a *arrayHashMap) hashFunc(key int) int {
index := key % 100
return index
}
/* Операция поиска */
func (a *arrayHashMap) get(key int) string {
index := a.hashFunc(key)
pair := a.buckets[index]
if pair == nil {
return "Not Found"
}
return pair.val
}
/* Операция добавления */
func (a *arrayHashMap) put(key int, val string) {
pair := &pair{key: key, val: val}
index := a.hashFunc(key)
a.buckets[index] = pair
}
/* Операция удаления */
func (a *arrayHashMap) remove(key int) {
index := a.hashFunc(key)
// Присвоить nil, что означает удаление
a.buckets[index] = nil
}
/* Получить все ключи */
func (a *arrayHashMap) pairSet() []*pair {
var pairs []*pair
for _, pair := range a.buckets {
if pair != nil {
pairs = append(pairs, pair)
}
}
return pairs
}
/* Получить все ключи */
func (a *arrayHashMap) keySet() []int {
var keys []int
for _, pair := range a.buckets {
if pair != nil {
keys = append(keys, pair.key)
}
}
return keys
}
/* Получить все значения */
func (a *arrayHashMap) valueSet() []string {
var values []string
for _, pair := range a.buckets {
if pair != nil {
values = append(values, pair.val)
}
}
return values
}
/* Вывести хеш-таблицу */
func (a *arrayHashMap) print() {
for _, pair := range a.buckets {
if pair != nil {
fmt.Println(pair.key, "->", pair.val)
}
}
}
/* Пара ключ-значение */
class Pair: Equatable {
public var key: Int
public var val: String
public init(key: Int, val: String) {
self.key = key
self.val = val
}
public static func == (lhs: Pair, rhs: Pair) -> Bool {
lhs.key == rhs.key && lhs.val == rhs.val
}
}
/* Хеш-таблица на основе массива */
class ArrayHashMap {
private var buckets: [Pair?]
init() {
// Инициализировать массив, содержащий 100 корзин
buckets = Array(repeating: nil, count: 100)
}
/* Хеш-функция */
private func hashFunc(key: Int) -> Int {
let index = key % 100
return index
}
/* Операция поиска */
func get(key: Int) -> String? {
let index = hashFunc(key: key)
let pair = buckets[index]
return pair?.val
}
/* Операция добавления */
func put(key: Int, val: String) {
let pair = Pair(key: key, val: val)
let index = hashFunc(key: key)
buckets[index] = pair
}
/* Операция удаления */
func remove(key: Int) {
let index = hashFunc(key: key)
// Присвоить nil, что означает удаление
buckets[index] = nil
}
/* Получить все пары ключ-значение */
func pairSet() -> [Pair] {
buckets.compactMap { $0 }
}
/* Получить все ключи */
func keySet() -> [Int] {
buckets.compactMap { $0?.key }
}
/* Получить все значения */
func valueSet() -> [String] {
buckets.compactMap { $0?.val }
}
/* Вывести хеш-таблицу */
func print() {
for pair in pairSet() {
Swift.print("\(pair.key) -> \(pair.val)")
}
}
}
/* Пара ключ-значение Number -> String */
class Pair {
constructor(key, val) {
this.key = key;
this.val = val;
}
}
/* Хеш-таблица на основе массива */
class ArrayHashMap {
#buckets;
constructor() {
// Инициализировать массив, содержащий 100 корзин
this.#buckets = new Array(100).fill(null);
}
/* Хеш-функция */
#hashFunc(key) {
return key % 100;
}
/* Операция поиска */
get(key) {
let index = this.#hashFunc(key);
let pair = this.#buckets[index];
if (pair === null) return null;
return pair.val;
}
/* Операция добавления */
set(key, val) {
let index = this.#hashFunc(key);
this.#buckets[index] = new Pair(key, val);
}
/* Операция удаления */
delete(key) {
let index = this.#hashFunc(key);
// Присвоить null, что означает удаление
this.#buckets[index] = null;
}
/* Получить все пары ключ-значение */
entries() {
let arr = [];
for (let i = 0; i < this.#buckets.length; i++) {
if (this.#buckets[i]) {
arr.push(this.#buckets[i]);
}
}
return arr;
}
/* Получить все ключи */
keys() {
let arr = [];
for (let i = 0; i < this.#buckets.length; i++) {
if (this.#buckets[i]) {
arr.push(this.#buckets[i].key);
}
}
return arr;
}
/* Получить все значения */
values() {
let arr = [];
for (let i = 0; i < this.#buckets.length; i++) {
if (this.#buckets[i]) {
arr.push(this.#buckets[i].val);
}
}
return arr;
}
/* Вывести хеш-таблицу */
print() {
let pairSet = this.entries();
for (const pair of pairSet) {
console.info(`${pair.key} -> ${pair.val}`);
}
}
}
/* Пара ключ-значение Number -> String */
class Pair {
public key: number;
public val: string;
constructor(key: number, val: string) {
this.key = key;
this.val = val;
}
}
/* Хеш-таблица на основе массива */
class ArrayHashMap {
private readonly buckets: (Pair | null)[];
constructor() {
// Инициализировать массив, содержащий 100 корзин
this.buckets = new Array(100).fill(null);
}
/* Хеш-функция */
private hashFunc(key: number): number {
return key % 100;
}
/* Операция поиска */
public get(key: number): string | null {
let index = this.hashFunc(key);
let pair = this.buckets[index];
if (pair === null) return null;
return pair.val;
}
/* Операция добавления */
public set(key: number, val: string) {
let index = this.hashFunc(key);
this.buckets[index] = new Pair(key, val);
}
/* Операция удаления */
public delete(key: number) {
let index = this.hashFunc(key);
// Присвоить null, что означает удаление
this.buckets[index] = null;
}
/* Получить все пары ключ-значение */
public entries(): (Pair | null)[] {
let arr: (Pair | null)[] = [];
for (let i = 0; i < this.buckets.length; i++) {
if (this.buckets[i]) {
arr.push(this.buckets[i]);
}
}
return arr;
}
/* Получить все ключи */
public keys(): (number | undefined)[] {
let arr: (number | undefined)[] = [];
for (let i = 0; i < this.buckets.length; i++) {
if (this.buckets[i]) {
arr.push(this.buckets[i].key);
}
}
return arr;
}
/* Получить все значения */
public values(): (string | undefined)[] {
let arr: (string | undefined)[] = [];
for (let i = 0; i < this.buckets.length; i++) {
if (this.buckets[i]) {
arr.push(this.buckets[i].val);
}
}
return arr;
}
/* Вывести хеш-таблицу */
public print() {
let pairSet = this.entries();
for (const pair of pairSet) {
console.info(`${pair.key} -> ${pair.val}`);
}
}
}
/* Пара ключ-значение */
class Pair {
int key;
String val;
Pair(this.key, this.val);
}
/* Хеш-таблица на основе массива */
class ArrayHashMap {
late List<Pair?> _buckets;
ArrayHashMap() {
// Инициализировать массив, содержащий 100 корзин
_buckets = List.filled(100, null);
}
/* Хеш-функция */
int _hashFunc(int key) {
final int index = key % 100;
return index;
}
/* Операция поиска */
String? get(int key) {
final int index = _hashFunc(key);
final Pair? pair = _buckets[index];
if (pair == null) {
return null;
}
return pair.val;
}
/* Операция добавления */
void put(int key, String val) {
final Pair pair = Pair(key, val);
final int index = _hashFunc(key);
_buckets[index] = pair;
}
/* Операция удаления */
void remove(int key) {
final int index = _hashFunc(key);
_buckets[index] = null;
}
/* Получить все пары ключ-значение */
List<Pair> pairSet() {
List<Pair> pairSet = [];
for (final Pair? pair in _buckets) {
if (pair != null) {
pairSet.add(pair);
}
}
return pairSet;
}
/* Получить все ключи */
List<int> keySet() {
List<int> keySet = [];
for (final Pair? pair in _buckets) {
if (pair != null) {
keySet.add(pair.key);
}
}
return keySet;
}
/* Получить все значения */
List<String> values() {
List<String> valueSet = [];
for (final Pair? pair in _buckets) {
if (pair != null) {
valueSet.add(pair.val);
}
}
return valueSet;
}
/* Вывести хеш-таблицу */
void printHashMap() {
for (final Pair kv in pairSet()) {
print("${kv.key} -> ${kv.val}");
}
}
}
/* Пара ключ-значение */
#[derive(Debug, Clone, PartialEq)]
pub struct Pair {
pub key: i32,
pub val: String,
}
/* Хеш-таблица на основе массива */
pub struct ArrayHashMap {
buckets: Vec<Option<Pair>>,
}
impl ArrayHashMap {
pub fn new() -> ArrayHashMap {
// Инициализировать массив, содержащий 100 корзин
Self {
buckets: vec![None; 100],
}
}
/* Хеш-функция */
fn hash_func(&self, key: i32) -> usize {
key as usize % 100
}
/* Операция поиска */
pub fn get(&self, key: i32) -> Option<&String> {
let index = self.hash_func(key);
self.buckets[index].as_ref().map(|pair| &pair.val)
}
/* Операция добавления */
pub fn put(&mut self, key: i32, val: &str) {
let index = self.hash_func(key);
self.buckets[index] = Some(Pair {
key,
val: val.to_string(),
});
}
/* Операция удаления */
pub fn remove(&mut self, key: i32) {
let index = self.hash_func(key);
// Присвоить None, что означает удаление
self.buckets[index] = None;
}
/* Получить все пары ключ-значение */
pub fn entry_set(&self) -> Vec<&Pair> {
self.buckets
.iter()
.filter_map(|pair| pair.as_ref())
.collect()
}
/* Получить все ключи */
pub fn key_set(&self) -> Vec<&i32> {
self.buckets
.iter()
.filter_map(|pair| pair.as_ref().map(|pair| &pair.key))
.collect()
}
/* Получить все значения */
pub fn value_set(&self) -> Vec<&String> {
self.buckets
.iter()
.filter_map(|pair| pair.as_ref().map(|pair| &pair.val))
.collect()
}
/* Вывести хеш-таблицу */
pub fn print(&self) {
for pair in self.entry_set() {
println!("{} -> {}", pair.key, pair.val);
}
}
}
/* Пара ключ-значение int->string */
typedef struct {
int key;
char *val;
} Pair;
/* Хеш-таблица на основе массива */
typedef struct {
Pair *buckets[MAX_SIZE];
} ArrayHashMap;
/* Конструктор */
ArrayHashMap *newArrayHashMap() {
ArrayHashMap *hmap = malloc(sizeof(ArrayHashMap));
for (int i=0; i < MAX_SIZE; i++) {
hmap->buckets[i] = NULL;
}
return hmap;
}
/* Деструктор */
void delArrayHashMap(ArrayHashMap *hmap) {
for (int i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
free(hmap->buckets[i]->val);
free(hmap->buckets[i]);
}
}
free(hmap);
}
/* Операция добавления */
void put(ArrayHashMap *hmap, const int key, const char *val) {
Pair *Pair = malloc(sizeof(Pair));
Pair->key = key;
Pair->val = malloc(strlen(val) + 1);
strcpy(Pair->val, val);
int index = hashFunc(key);
hmap->buckets[index] = Pair;
}
/* Операция удаления */
void removeItem(ArrayHashMap *hmap, const int key) {
int index = hashFunc(key);
free(hmap->buckets[index]->val);
free(hmap->buckets[index]);
hmap->buckets[index] = NULL;
}
/* Получить все пары ключ-значение */
void pairSet(ArrayHashMap *hmap, MapSet *set) {
Pair *entries;
int i = 0, index = 0;
int total = 0;
/* Подсчитать число действительных пар ключ-значение */
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
entries = malloc(sizeof(Pair) * total);
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
entries[index].key = hmap->buckets[i]->key;
entries[index].val = malloc(strlen(hmap->buckets[i]->val) + 1);
strcpy(entries[index].val, hmap->buckets[i]->val);
index++;
}
}
set->set = entries;
set->len = total;
}
/* Получить все ключи */
void keySet(ArrayHashMap *hmap, MapSet *set) {
int *keys;
int i = 0, index = 0;
int total = 0;
/* Подсчитать число действительных пар ключ-значение */
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
keys = malloc(total * sizeof(int));
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
keys[index] = hmap->buckets[i]->key;
index++;
}
}
set->set = keys;
set->len = total;
}
/* Получить все значения */
void valueSet(ArrayHashMap *hmap, MapSet *set) {
char **vals;
int i = 0, index = 0;
int total = 0;
/* Подсчитать число действительных пар ключ-значение */
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
total++;
}
}
vals = malloc(total * sizeof(char *));
for (i = 0; i < MAX_SIZE; i++) {
if (hmap->buckets[i] != NULL) {
vals[index] = hmap->buckets[i]->val;
index++;
}
}
set->set = vals;
set->len = total;
}
/* Вывести хеш-таблицу */
void print(ArrayHashMap *hmap) {
int i;
MapSet set;
pairSet(hmap, &set);
Pair *entries = (Pair *)set.set;
for (i = 0; i < set.len; i++) {
printf("%d -> %s\n", entries[i].key, entries[i].val);
}
free(set.set);
}
/* Пара ключ-значение */
class Pair(
var key: Int,
var _val: String
)
/* Хеш-таблица на основе массива */
class ArrayHashMap {
// Инициализировать массив, содержащий 100 корзин
private val buckets = arrayOfNulls<Pair>(100)
/* Хеш-функция */
fun hashFunc(key: Int): Int {
val index = key % 100
return index
}
/* Операция поиска */
fun get(key: Int): String? {
val index = hashFunc(key)
val pair = buckets[index] ?: return null
return pair._val
}
/* Операция добавления */
fun put(key: Int, _val: String) {
val pair = Pair(key, _val)
val index = hashFunc(key)
buckets[index] = pair
}
/* Операция удаления */
fun remove(key: Int) {
val index = hashFunc(key)
// Присвоить null, что означает удаление
buckets[index] = null
}
/* Получить все пары ключ-значение */
fun pairSet(): MutableList<Pair> {
val pairSet = mutableListOf<Pair>()
for (pair in buckets) {
if (pair != null)
pairSet.add(pair)
}
return pairSet
}
/* Получить все ключи */
fun keySet(): MutableList<Int> {
val keySet = mutableListOf<Int>()
for (pair in buckets) {
if (pair != null)
keySet.add(pair.key)
}
return keySet
}
/* Получить все значения */
fun valueSet(): MutableList<String> {
val valueSet = mutableListOf<String>()
for (pair in buckets) {
if (pair != null)
valueSet.add(pair._val)
}
return valueSet
}
/* Вывести хеш-таблицу */
fun print() {
for (kv in pairSet()) {
val key = kv.key
val _val = kv._val
println("$key -> $_val")
}
}
}
### Пара ключ-значение ###
class Pair
attr_accessor :key, :val
def initialize(key, val)
@key = key
@val = val
end
end
### Хеш-таблица на основе массива ###
class ArrayHashMap
### Конструктор ###
def initialize
# Инициализировать массив, содержащий 100 корзин
@buckets = Array.new(100)
end
### Хеш-функция ###
def hash_func(key)
index = key % 100
end
### Операция поиска ###
def get(key)
index = hash_func(key)
pair = @buckets[index]
return if pair.nil?
pair.val
end
### Операция добавления ###
def put(key, val)
pair = Pair.new(key, val)
index = hash_func(key)
@buckets[index] = pair
end
### Операция удаления ###
def remove(key)
index = hash_func(key)
# Присвоить nil, что означает удаление
@buckets[index] = nil
end
### Получить все пары ключ-значение ###
def entry_set
result = []
@buckets.each { |pair| result << pair unless pair.nil? }
result
end
### Получить все ключи ###
def key_set
result = []
@buckets.each { |pair| result << pair.key unless pair.nil? }
result
end
### Получить все значения ###
def value_set
result = []
@buckets.each { |pair| result << pair.val unless pair.nil? }
result
end
### Вывести хеш-таблицу ###
def print
@buckets.each { |pair| puts "#{pair.key} -> #{pair.val}" unless pair.nil? }
end
end
Визуализация кода
6.1.3 Хеш-коллизии и расширение¶
По сути, хеш-функция отображает входное пространство, состоящее из всех key , в выходное пространство, состоящее из всех индексов массива, а входное пространство обычно значительно больше выходного. Поэтому теоретически неизбежно существование ситуации "несколько входов соответствуют одному выходу".
Для хеш-функции из приведенного выше примера, если последние две цифры key совпадают, то совпадает и результат хеш-функции. Например, если искать студентов с номерами 12836 и 20336, то получим:
Как показано на рисунке 6-3, два номера указывают на одно и то же имя, что, очевидно, неверно. Такую ситуацию, когда нескольким входам соответствует один и тот же выход, называют хеш-коллизией (hash collision).

Рисунок 6-3 Пример хеш-коллизии
Легко понять, что чем больше емкость хеш-таблицы \(n\) , тем ниже вероятность того, что несколько key попадут в один и тот же бакет, а значит, тем меньше коллизий. Поэтому мы можем уменьшать число хеш-коллизий путем расширения хеш-таблицы.
Как показано на рисунке 6-4, до расширения пары ключ-значение (136, A) и (236, D) конфликтовали, а после расширения коллизия исчезла.

Рисунок 6-4 Расширение хеш-таблицы
Подобно расширению массива, расширение хеш-таблицы требует перенести все пары ключ-значение из старой таблицы в новую, а это очень затратно по времени; кроме того, поскольку емкость хеш-таблицы capacity изменилась, нам приходится с помощью хеш-функции заново вычислять позиции хранения всех пар ключ-значение, что дополнительно увеличивает вычислительные расходы процесса расширения. Поэтому языки программирования обычно заранее резервируют достаточно большую емкость хеш-таблицы, чтобы избежать частых расширений.
Коэффициент загрузки (load factor) - важное понятие хеш-таблицы. Он определяется как отношение числа элементов в хеш-таблице к числу бакетов и используется для оценки степени серьезности хеш-коллизий, а также часто служит условием срабатывания расширения хеш-таблицы. Например, в Java, когда коэффициент загрузки превышает \(0.75\) , система расширяет хеш-таблицу до \(2\) раз от исходной емкости.