Библиотека knigago >> Компьютеры: Разработка ПО >> Алгоритмы и структуры данных >> Хеш-таблицы


Ирина Вечерская Кулинария Книга "100 рецептов блюд, богатых витамином B" - это настоящий клад для тех, кто заботится о своем здоровье и стремится питаться вкусно и полезно. Автор Ирина Вечерская предлагает широкий выбор разнообразных блюд, которые не только удовлетворят ваш аппетит, но и обеспечат организм жизненно важным витамином В. * Информативную главу об основных свойствах и значении витамина В. * 100 оригинальных и аппетитных рецептов, разделенных по категориям: *...

СЛУЧАЙНАЯ КНИГА

Иван Кисляков (Программист) - Хеш-таблицы

Хеш-таблицы
Книга - Хеш-таблицы.  Иван Кисляков (Программист)  - прочитать полностью в библиотеке КнигаГо
Название:
Хеш-таблицы
Иван Кисляков (Программист)

Жанр:

Статьи и рефераты, Самиздат, сетевая литература, Литература ХXI века (эпоха Глобализации экономики), Алгоритмы и структуры данных, C, C++, C#

Изадано в серии:

неизвестно

Издательство:

Интернет-издательство «Stribog»

Год издания:

ISBN:

неизвестно

Отзывы:

Комментировать

Рейтинг:

Поделись книгой с друзьями!

Помощь сайту: донат на оплату сервера

Краткое содержание книги "Хеш-таблицы"

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

Читаем онлайн "Хеш-таблицы". [Страница - 2]

стр.
int table_size, const int key)

{

    int hash_result = 0;

    for (int i = 0; s[i] != s.size(); ++i)

        hash_result = (key * hash_result + s[i]) % table_size;

    hash_result = (hash_result * 2 + 1) % table_size;

    return hash_result;

}

struct HashFunction1

{

    int operator()(const std::string& s, int table_size) const

    {

        return HashFunctionHorner(s, table_size, table_size - 1);

        // ключи должны быть взаимопросты, используем числа

        // <размер таблицы> плюс и минус один.

    }

};

struct HashFunction2

{

    int operator()(const std::string& s, int table_size) const

    {

        return HashFunctionHorner(s, table_size, table_size + 1);

    }

};


Чтобы идти дальше, нам необходимо разобраться с проблемой: что же будет, если мы удалим элемент из таблицы? Так вот, его нужно пометить флагом deleted, но просто удалять его безвозвратно нельзя. Ведь если мы так сделаем, то при попытке найти элемент (значение хеш-функции которого совпадет с ее значением у нашего удаленного элемента) мы сразу наткнемся на пустую ячейку. А это значит, что такого элемента и не было никогда, хотя, он лежит, просто где-то дальше в массиве. Это основная сложность использования данного метода решения коллизий.

Помня о данной проблеме построим наш класс.


template <class T, class THash1 = HashFunction1, class THash2 = HashFunction2>

class HashTable

{

    static const int default_size = 8; // начальный размер нашей таблицы

    constexpr static const double rehash_size = 0.75; // коэффициент, при котором произойдет увеличение таблицы

    struct Node

    {

        T value;

        bool state; // если значение флага state = false, значит элемент массива был удален (deleted)

        Node(const T& value_) : value(value_), state(true) {}

    };

    Node** arr; // соответственно в массиве будут хранится структуры Node*

    int size;   // сколько элементов у нас сейчас в массиве (без учета deleted)

    int buffer_size; // размер самого массива, сколько памяти выделено под хранение нашей таблицы

    int size_all_non_nullptr; // сколько элементов у нас сейчас в массиве (с учетом deleted)

};


На данном этапе мы уже более-менее поняли, что у нас будет храниться в таблице. Переходим к реализации служебных методов.


...

public:

    HashTable()

    {

        buffer_size = default_size;

        size = 0;

        size_all_non_nullptr = 0;

        arr = new Node*[buffer_size];

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

            arr[i] = nullptr; // заполняем nullptr - то есть если значение отсутствует, и никто раньше по этому адресу не обращался

    }

    ~HashTable()

    {

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

            if (arr[i])

                delete arr[i];

        delete[] arr;

    }


Из необходимых методов осталось еще реализовать динамическое увеличение, расширение массива — метод Resize.

Увеличиваем размер мы стандартно вдвое.


void Resize()

    {

        int past_buffer_size = buffer_size;

        buffer_size *= 2;

        size_all_non_nullptr = 0;

        size = 0;

        Node** arr2 = new Node * [buffer_size];

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

            arr2[i] = nullptr;

        std::swap(arr, arr2);

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

        {

            if (arr2[i] && arr2[i]->state)

                Add(arr2[i]->value); // добавляем элементы в новый массив

        }

        // удаление предыдущего массива

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

            if (arr2[i])

                delete arr2[i];

        delete[] arr2;

    }


Немаловажным является поддержание асимптотики O(1) стандартных операций. Но что же может повлиять на скорость работы? Наши удаленные элементы (deleted). Ведь, как мы помним, мы ничего не можем с ними сделать, но и окончательно обнулить их не можем. Так что они тянутся за нами огромным балластом. Для ускорения работы нашей хеш-таблицы воспользуемся рехешем (как мы помним, мы уже выделяли под это очень странные переменные).

Теперь воспользуемся ими, если процент реальных элементов массива стал меньше 50, мы производим Rehash, а именно делаем то же самое, что и при увеличении таблицы (resize), но не увеличиваем. Возможно, это звучит глуповато, но попробую сейчас объяснить. Мы вызовем наши хеш-функции от всех элементов, переместим их в новых массив. Но с deleted-элементами это не произойдет, мы не будем их перемещать, и они удалятся вместе со старой таблицей.

Но к чему слова, код все разъяснит:


void Rehash()

    {

        size_all_non_nullptr = 0;

        size = 0;

        Node** arr2 = new Node * [buffer_size];

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

            arr2[i] = nullptr;

        std::swap(arr, arr2);

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

        {

            if (arr2[i] && arr2[i]->state)

                Add(arr2[i]->value);

--">
стр.

Оставить комментарий:


Ваш e-mail является приватным и не будет опубликован в комментарии.