Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

К9-12В. Вопросы и ответы к ГОСам 2013 / Программирование на языке высокого уровня / 09. Перемешанная таблица, использующая перемешиванием сцеплением. Определение, хэш-функция, возникающие проблемы, основн

.docx
Скачиваний:
126
Добавлен:
10.05.2014
Размер:
57.8 Кб
Скачать

09. Перемешанная таблица, использующая перемешиванием сцеплением. Определение, хэш-функция, возникающие проблемы, основные операции, особенности их реализации.

Перемешанные таблицы

Поскольку взаимную однозначность преобразования ключа в адрес хранения элемента таблицы в общем случае обеспечить практически невозможно, от требования взаимной однозначности отказываются. Это приводит к тому, что для некоторых kikj возможна ситуация, что I(ki) = I(kj). Такая ситуация создает переполнение позиций отображаемого вектора и носит название коллизии. Чтобы таких ситуаций было меньше, функцию расстановки подбирают из условия возможно более равномерного отображения ключей в адреса хранения. Таблицы, построенные по такому принципу, также являются таблицами с вычисляемыми входами и называются перемешаннымитаблицами.

Перемешанную таблицу можно заполнять и обращаться с ней как с таблицей произвольного доступа до тех пор, пока не встретится ключ kj такой, что kikj, но I(ki) = I(kj), причем ki – это ключ, который уже встречался ранее (т.е. пока не возникнет коллизия). Элемент с ключом ki уже был помещен в позицию (элемент вектора с индексом) I(ki). Проблема состоит в определении места для хранения нового элемента таблицы с ключом kj. Решение этой проблемы предполагает разрешение коллизии и носит название перемешивания. Существуют различные вспомогательные методы перемешивания, зависящие от того, какой способ используется для разрешения коллизии.

Часто перемешанные таблицы называют еще хэш таблицами (от английского hash–мешанина, путаница), функции расстановки – хэш функциями, а нахождение места хранения элемента – хешированием.

В перемешанной таблице можно выделить две области: основную, в которую элементы таблицы отображаются в результате вычисления производного ключа, и область переполнения, в которую попадают элементы в результате перемешивания при обнаружении коллизии. В зависимости от используемого способа перемешивания такое разделение может быть явным или скрытым. В любом случае основная область перемешанной таблицы отображается вектором. Область переполнения может быть отображена и вектором, и списком (точнее, семейством списков), в зависимости от того, какой способ перемешивания используется.

В каждом конкретном случае при использовании перемешанной таблицы используется какой-то один способ перемешивания. Он используется как при поиске элемента в таблице, так и при включении в таблицу нового элемента.

Рассмотрим два способа перемешивания, используемых наиболее часто: открытое перемешивание (этот способ называют еще перемешиванием сложением) и перемешивание сцеплением.

Перемешивание сцеплением

Перемешанная таблица, использующая перемешивание сцеплением, обладает следующими свойствами:

  1. Таблица явно разделяется на две области – основную область и область переполнения.

  2. Основная область таблицы отображается вектором, размер которого должен быть достаточным для отображения всех значений ключей.

  3. Область переполнения представляет собой семейство списков – отдельный список для каждого элемента основной области.

Следовательно, элемент перемешанной таблицы, использующей перемешивание сцеплением, должен иметь еще одно поле – поле связи с элементами из области переполнения (рис. II-49).

Рассмотрим способ перемешивания сцеплением на примере выполнения операции включения элементов в таблицу.

Пусть дана та же, что и ранее, последовательность ключей элементов, записываемых в таблицу (всего 15 элементов):

12, 48, 3, 5, 7, 63, 15, 202, 103, 188, 30, 43, 6, 18, 19

Используем ту же функцию расстановки:

I(k) = k%10;

В соответствии с выбранной функцией расстановки основная область таблицы будет представлена вектором из 10 элементов.

Функция расстановки отобразит исходное множество ключей в следующее множество производных ключей:

номер элемента

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

исходный ключ

12

48

3

5

7

63

15

202

103

188

30

43

6

18

19

производный ключ

2

8

3

5

7

3

5

2

3

8

0

3

6

8

9

При занесении записей в таблицу, пока не встретится коллизия (до записи с ключом 63), записи будут включаться в основную область. В случае коллизии запись будет включена в список, связанный с соответствующим элементом основной таблицы, причем переполняющая запись может быть включена и в начало, и в конец соответствующего списка.

В результате занесения всех перечисленных записей таблица примет вид, приведенный на рис. II-50 (для наглядности переполняющие записи включаются в конец соответствующего списка).

Из приведенного примера видно, что записи, ключи которых имеют одинаковое значение производного ключа, объединяются в один список; следовательно, при поиске записи потребуется просматривать только записи таблицы с одинаковым значением производного ключа. В цитировавшейся выше книге Ф.Хопгуда (Методы компиляции, стр. 36) приведена следующая оценка средней длины поиска для случайных, равномерно распределенных записей:

где M – размер основной области таблицы (отображающего вектора),

N – общее количество элементов в таблице.

На практике отображающий вектор (основную область таблицы) удобно представить только массивом указателей на элементы таблицы, а все записи включать в область переполнения. В этом случае все операции с таблицей сначала осуществляют выбор необходимого списка (элемента массива указателей), а затем реализуется требуемая операция со списком (соответствующие операции со списками были рассмотрены ранее). Приведенная выше таблица в таком случае будет представлена следующим образом:

Предлагаю самостоятельно разработать и реализовать основные операции работы с перемешанной таблицей, использующей перемешивание сцеплением.

struct overflowTableItem{

int key;

int release;

size_t sizeinfo;

long position;

overflowTableItem *next;

};

overflowTableItem *table[MAX_ITEM];

int hashFunction(int key){

return key % MAX_ITEM;

}

for(int i=0; i < MAX_ITEM; i++)

table[i] = NULL;

int tableInsert(overflowTableItem *table[],int key, string info){

int position = hashFunction(key);

int release;

overflowTableItem *tmpItem, *lastItem = NULL;

overflowTableItem *item = new overflowTableItem;

item->key = key;

item->info = info;

if(table[position] == NULL){

item->release = 1;

item->next = NULL;

table[position] = item;

}

else{

tmpItem = table[position];

release = 0;

while(tmpItem){

if(tmpItem->key == key){

release = tmpItem->release;

if(tmpItem->next != NULL && tmpItem->next->key != key) break;

}

if(tmpItem->next == NULL) break;

tmpItem = tmpItem->next;

}

item->release = ++release;

item->next = tmpItem->next;

tmpItem->next = item;

}

return 0;

}

int findByKey(overflowTableItem *table[],int key){

int position = hashFunction(key);

bool flag = false;

overflowTableItem *tmpItem;

if(table[position] == NULL){

cout << endl << "not found" << endl;

return 1;

}

else{

tmpItem = table[position];

while(tmpItem){

if(tmpItem->key == key){

cout << endl << "[k: " << tmpItem->key << " r: " << tmpItem->release << " i: " << tmpItem->info << "] ";

flag = true;

}

tmpItem = tmpItem->next;

}

if(!flag) cout << endl << "not found";

cout << endl;

}

return 0;

}

int findByKeyAndRelease(overflowTableItem *table[], int key, int release){

int position = hashFunction(key);

bool flag = false;

overflowTableItem *tmpItem;

if(table[position] == NULL){

cout << endl << "not found" << endl;

return 1;

}

else{

tmpItem = table[position];

while(tmpItem){

if(tmpItem->key == key && tmpItem->release == release){

cout << endl << "[k: " << tmpItem->key << " r: " << tmpItem->release << " i: " << tmpItem->info << "] " << endl;;

flag = true;

return 0;

}

tmpItem = tmpItem->next;

}

if(!flag) cout << endl << "not found";

cout << endl;

}

return 0;

}

void viewTable(overflowTableItem *table[]){

overflowTableItem *tmpItem;

cout << endl;

for(int i=0; i<MAX_ITEM; i++){

if(table[i]){

cout << "[" << i << "]";

tmpItem = table[i];

while(tmpItem){

cout << " -> [k: " << tmpItem->key << " r: " << tmpItem->release << " i: " << tmpItem->info << "] ";

tmpItem = tmpItem->next;

}

cout << endl;

}

else cout << "[" << i << "] " << "empty" << endl;

}

}

void clearTable(overflowTableItem *table[]){

overflowTableItem *tmpItem, *prevItem = NULL, *trash;

for(int i=0; i<MAX_ITEM; i++){

if(table[i]){

tmpItem = table[i];

prevItem = NULL;

while(tmpItem){

if(tmpItem->next != NULL && tmpItem->next->key == tmpItem->key){

if(prevItem == NULL){

table[i] = tmpItem->next;

trash = tmpItem;

tmpItem = tmpItem->next;

delete trash;

continue;

}

else{

trash = tmpItem;

prevItem->next = tmpItem->next;

tmpItem = tmpItem->next;

delete trash;

continue;

}

}

prevItem = tmpItem;

tmpItem = tmpItem->next;

}

}

}

}

Соседние файлы в папке Программирование на языке высокого уровня