127 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			127 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								title: Hash Tables
							 | 
						|||
| 
								 | 
							
								localeTitle: Хэш-таблицы
							 | 
						|||
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								## Хэш-таблицы
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Хэш-таблица (или хэш-карта) представляет собой структуру данных, которая может сопоставлять ключи с значениями. В хеш-таблице используется хэш-функция для вычисления индекса в массив ведер, из которых можно найти нужные значения. Временная сложность хорошо определенной функции Хэша может быть O (1).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Хэш-таблица (хэш-карта) представляет собой структуру данных, которая реализует абстрактный тип абстрактных массивов, структуру, которая может сопоставлять ключи со значениями. Хэш-таблица использует хеш-функцию для вычисления индекса в массив ведер или слотов, из которого можно найти желаемое значение.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Некоторые важные свойства Hash Table - 1) Значения не сохраняются в отсортированном порядке. 2) В хэш-таблице нужно также обрабатывать потенциальные столкновения. Это часто делается путем цепочки, что означает создание связанного списка всех значений, ключи которых сопоставляются с определенным индексом.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Реализация таблицы хешей
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Хэш-таблица традиционно реализуется с массивом связанных списков. Когда мы хотим вставить пару ключ / значение, мы сопоставляем ключ с индексом в массиве с помощью хэш-функции. Затем значение вставляется в связанный список в этой позиции.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Идея хеширования состоит в том, чтобы распределять записи (пары ключ / значение) по массиву ведер. Учитывая ключ, алгоритм вычисляет индекс, который указывает, где можно найти запись:
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								index = f(key, array_size) 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Часто это делается в два этапа:
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								hash = hashfunc(key) 
							 | 
						|||
| 
								 | 
							
								 index = hash % array_size 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								В этом методе хэш не зависит от размера массива, а затем сводится к индексу (число от 0 до array\_size - 1) с использованием оператора modulo (%).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Рассмотрим строку S. Вам необходимо подсчитать частоту всех символов в этой строке.
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								string S = “ababcd” 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Самый простой способ сделать это - перебрать все возможные символы и подсчитать их частоту один за другим. Сложность времени этого подхода O (26 \* N), где N - размер строки, и имеется 26 возможных символов.
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								void countFre(string S) 
							 | 
						|||
| 
								 | 
							
								    { 
							 | 
						|||
| 
								 | 
							
								        for(char c = 'a';c <= 'z';++c) 
							 | 
						|||
| 
								 | 
							
								        { 
							 | 
						|||
| 
								 | 
							
								            int frequency = 0; 
							 | 
						|||
| 
								 | 
							
								            for(int i = 0;i < S.length();++i) 
							 | 
						|||
| 
								 | 
							
								                if(S[i] == c) 
							 | 
						|||
| 
								 | 
							
								                    frequency++; 
							 | 
						|||
| 
								 | 
							
								            cout << c << ' ' << frequency << endl; 
							 | 
						|||
| 
								 | 
							
								        } 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Вывод
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								a 2 
							 | 
						|||
| 
								 | 
							
								 b 2 
							 | 
						|||
| 
								 | 
							
								 c 1 
							 | 
						|||
| 
								 | 
							
								 d 1 
							 | 
						|||
| 
								 | 
							
								 e 0 
							 | 
						|||
| 
								 | 
							
								 f 0 
							 | 
						|||
| 
								 | 
							
								 … 
							 | 
						|||
| 
								 | 
							
								 z 0 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Давайте применим хеширование к этой проблеме. Возьмите частоту массива размером 26 и хэш 26 символов с индексами массива с помощью хэш-функции. Затем перебираем строку и увеличиваем значение в частоте по соответствующему индексу для каждого символа. Сложность этого подхода - O (N), где N - размер строки.
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								int Frequency[26]; 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    int hashFunc(char c) 
							 | 
						|||
| 
								 | 
							
								    { 
							 | 
						|||
| 
								 | 
							
								        return (c - 'a'); 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    void countFre(string S) 
							 | 
						|||
| 
								 | 
							
								    { 
							 | 
						|||
| 
								 | 
							
								        for(int i = 0;i < S.length();++i) 
							 | 
						|||
| 
								 | 
							
								        { 
							 | 
						|||
| 
								 | 
							
								            int index = hashFunc(S[i]); 
							 | 
						|||
| 
								 | 
							
								            Frequency[index]++; 
							 | 
						|||
| 
								 | 
							
								        } 
							 | 
						|||
| 
								 | 
							
								        for(int i = 0;i < 26;++i) 
							 | 
						|||
| 
								 | 
							
								            cout << (char)(i+'a') << ' ' << Frequency[i] << endl; 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Вывод
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								a 2 
							 | 
						|||
| 
								 | 
							
								 b 2 
							 | 
						|||
| 
								 | 
							
								 c 1 
							 | 
						|||
| 
								 | 
							
								 d 1 
							 | 
						|||
| 
								 | 
							
								 e 0 
							 | 
						|||
| 
								 | 
							
								 f 0 
							 | 
						|||
| 
								 | 
							
								 … 
							 | 
						|||
| 
								 | 
							
								 z 0 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### Хэш-столкновение
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Когда вы используете хэш-карту, вы должны предположить, что хеш-коллизии неизбежны, так как вы будете использовать хэш-карту, которая значительно меньше по размеру, чем объем данных, которые у вас есть. Двумя основными подходами к решению этих столкновений являются цепочка и открытая адресация.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### Цепной
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Один из способов разрешения хеш-коллизий - использование цепочки. Это означает, что для каждого сопоставления значений ключа в хэш-таблице поле значения не будет содержать только одну ячейку данных, а скорее связанный список данных. В примере, показанном на рисунке ниже, вы можете видеть, что Сандра Ди добавляется в качестве другого элемента к ключу 152 после Джона Смита.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Основным препятствием для цепочки является увеличение временной сложности. Это означает, что вместо свойств O (1) регулярной хеш-таблицы каждое действие будет занимать больше времени, поскольку нам нужно пересечь связанный список.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### Открытая адресация
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Другой способ разрешения хэш-коллизий - использовать открытую адресацию. В этом методе, когда значение отображается на уже занятый ключ, вы перемещаетесь по соседним клавишам хэш-таблицы определенным образом, пока не найдете ключ с пустым значением. В примере, показанном на изображении ниже, Сандра Ди отображается на ключ 153, хотя ее значение должно быть отображено на 152.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Основная проблема открытой адресации заключается в том, что, когда нужно искать значения, они могут оказаться не такими, какие вы ожидаете от них (сопоставление ключей). Поэтому вам нужно пройти части хэш-таблицы, чтобы найти нужное вам значение, что привело к увеличению временной сложности.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### Сложность времени
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Очень важно отметить, что хеш-таблицы амортизируют постоянную сложность, т. Е. В среднем случае сложность будет равна O (1). В худшем случае, если слишком много элементов было хэшировано в один и тот же ключ, это может иметь временную сложность O (n).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### Дополнительная информация:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								[Дополнительная информация о Hash Tables - Wiki](https://en.wikipedia.org/wiki/Hash_table) [Сравнение между таблицей хэшей и STL-картой](http://www.geeksforgeeks.org/hash-table-vs-stl-map/)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### Источник
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								[Основы Hash Tables - HackerEarth](https://www.hackerearth.com/practice/data-structures/hash-tables/basics-of-hash-tables/tutorial/)
							 |