mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-25 09:37:14 +00:00
build
This commit is contained in:
@@ -1526,7 +1526,7 @@ comments: true
|
||||
|
||||
## 6.2.2 Открытая адресация
|
||||
|
||||
<u>Открытая адресация (open addressing)</u> не вводит дополнительных структур данных, а обрабатывает хеш-коллизии с помощью многократного пробирования; основные варианты пробирования включают линейное пробирование, квадратичное пробирование и повторное хеширование.
|
||||
<u>Открытая адресация (open addressing)</u> не вводит дополнительных структур данных, а обрабатывает хеш-коллизии с помощью многократного пробирования. Основные варианты пробирования включают линейное пробирование, квадратичное пробирование и повторное хеширование.
|
||||
|
||||
Ниже на примере линейного пробирования рассмотрим механизм работы хеш-таблицы с открытой адресацией.
|
||||
|
||||
@@ -1534,8 +1534,8 @@ comments: true
|
||||
|
||||
Линейное пробирование использует линейный поиск с фиксированным шагом. Его методы работы отличаются от обычной хеш-таблицы.
|
||||
|
||||
- **Вставка элемента**: по хеш-функции вычисляется индекс бакета; если бакет уже занят, то от места конфликта выполняется линейный обход вперед (шаг обычно равен $1$ ), пока не будет найден пустой бакет, после чего элемент вставляется туда.
|
||||
- **Поиск элемента**: если возник конфликт, то с тем же шагом продолжается линейный обход вперед, пока не будет найден целевой элемент и возвращено `value` ; если встречается пустой бакет, это означает, что искомого элемента в хеш-таблице нет, и возвращается `None` .
|
||||
- **Вставка элемента**: по хеш-функции вычисляется индекс бакета. Если бакет уже занят, то от места конфликта выполняется линейный обход вперед (шаг обычно равен $1$ ), пока не будет найден пустой бакет, после чего элемент вставляется туда.
|
||||
- **Поиск элемента**: если возник конфликт, то с тем же шагом продолжается линейный обход вперед, пока не будет найден целевой элемент и возвращено `value`. Если встречается пустой бакет, это означает, что искомого элемента в хеш-таблице нет, и возвращается `None` .
|
||||
|
||||
На рисунке 6-6 показано распределение пар ключ-значение в хеш-таблице с открытой адресацией (линейное пробирование). Для этой хеш-функции все `key` с одинаковыми двумя последними цифрами отображаются в один и тот же бакет. Благодаря линейному пробированию они по очереди сохраняются в этом бакете и в следующих за ним бакетах.
|
||||
|
||||
@@ -1553,7 +1553,7 @@ comments: true
|
||||
|
||||
Чтобы решить эту проблему, можно использовать механизм <u>ленивого удаления (lazy deletion)</u>: он не удаляет элемент из хеш-таблицы напрямую, **а помечает этот бакет специальной константой `TOMBSTONE` **. В этом механизме и `None` , и `TOMBSTONE` означают пустой бакет, и оба могут быть использованы для размещения пары ключ-значение. Но есть важное различие: при линейном пробировании, встретив `TOMBSTONE` , нужно продолжать обход, потому что ниже него все еще могут существовать пары ключ-значение.
|
||||
|
||||
Однако **ленивое удаление может ускорять деградацию производительности хеш-таблицы**. Это связано с тем, что каждая операция удаления создает новую метку удаления; по мере роста числа `TOMBSTONE` время поиска тоже увеличивается, потому что линейное пробирование может быть вынуждено перескакивать через множество `TOMBSTONE` , прежде чем найдет целевой элемент.
|
||||
Однако **ленивое удаление может ускорять деградацию производительности хеш-таблицы**. Это связано с тем, что каждая операция удаления создает новую метку удаления. По мере роста числа `TOMBSTONE` время поиска тоже увеличивается, потому что линейное пробирование может быть вынуждено перескакивать через множество `TOMBSTONE` , прежде чем найдет целевой элемент.
|
||||
|
||||
Поэтому имеет смысл при линейном пробировании запоминать индекс первого встреченного `TOMBSTONE` и затем менять найденный целевой элемент местами с этим `TOMBSTONE` . Преимущество такого подхода в том, что при каждом поиске или добавлении элемент будет перемещаться в бакет, расположенный ближе к его идеальной позиции (начальной точке пробирования), а значит, эффективность поиска улучшится.
|
||||
|
||||
@@ -3284,7 +3284,7 @@ comments: true
|
||||
|
||||
### 2. Квадратичное пробирование
|
||||
|
||||
Квадратичное пробирование похоже на линейное пробирование и тоже является одной из распространенных стратегий открытой адресации. При возникновении конфликта оно не пропускает фиксированное число шагов, а переходит на расстояние, равное "квадрату числа попыток", то есть на $1, 4, 9, \dots$ шагов.
|
||||
Квадратичное пробирование похоже на линейное пробирование и тоже является одной из распространенных стратегий открытой адресации. При возникновении конфликта оно не пропускает фиксированное число шагов, а переходит на расстояние, равное «квадрату числа попыток», то есть на $1, 4, 9, \dots$ шагов.
|
||||
|
||||
Квадратичное пробирование имеет следующие основные преимущества.
|
||||
|
||||
@@ -3301,7 +3301,7 @@ comments: true
|
||||
Как видно из названия, метод повторного хеширования использует для пробирования несколько хеш-функций $f_1(x)$, $f_2(x)$, $f_3(x)$, $\dots$ .
|
||||
|
||||
- **Вставка элемента**: если хеш-функция $f_1(x)$ вызывает конфликт, то пробуем $f_2(x)$ , и так далее, пока не будет найдено пустое место для вставки элемента.
|
||||
- **Поиск элемента**: поиск идет в том же порядке хеш-функций, пока не будет найден целевой элемент; если встречается пустая позиция или уже были опробованы все хеш-функции, это означает, что элемента в хеш-таблице нет, и возвращается `None` .
|
||||
- **Поиск элемента**: поиск идет в том же порядке хеш-функций, пока не будет найден целевой элемент. Если встречается пустая позиция или уже были опробованы все хеш-функции, это означает, что элемента в хеш-таблице нет, и возвращается `None` .
|
||||
|
||||
По сравнению с линейным пробированием метод повторного хеширования меньше подвержен кластеризации, но несколько хеш-функций приносят дополнительные вычислительные затраты.
|
||||
|
||||
@@ -3315,4 +3315,4 @@ comments: true
|
||||
|
||||
- Python использует открытую адресацию. В словаре `dict` для пробирования применяются псевдослучайные числа.
|
||||
- Java использует метод цепочек. Начиная с JDK 1.8, когда длина массива внутри `HashMap` достигает 64, а длина списка достигает 8, этот список преобразуется в красно-черное дерево для повышения производительности поиска.
|
||||
- Go использует метод цепочек. В Go установлено, что каждый бакет может хранить не более 8 пар ключ-значение; при переполнении подключается overflow-бакет, а когда таких бакетов становится слишком много, выполняется специальное расширение того же масштаба, чтобы сохранить производительность.
|
||||
- Go использует метод цепочек. В Go установлено, что каждый бакет может хранить не более 8 пар ключ-значение. При переполнении подключается overflow-бакет, а когда таких бакетов становится слишком много, выполняется специальное расширение того же масштаба, чтобы сохранить производительность.
|
||||
|
||||
Reference in New Issue
Block a user