- •А. С. Третьяков алгоритмы
- •Структуры данных Оглавление
- •2.1 Введение 20
- •4.1 Введение 69
- •5.1 Введение 87
- •Предисловие
- •Раздел I структуры данных
- •Глава 1. Массивы
- •1.1 Введение
- •1.2.1 Метод деления
- •1.2.2 Метод умножения
- •1.3.1 Открытое хеширование
- •1.3.2 Закрытое хеширование
- •1.4 Ассоциативный массив
- •1.5 Динамический массив
- •Глава 2. Списки
- •2.1 Введение
- •2.2 Связный список
- •2.3 Стек
- •2.4 Очередь
- •2.4.1 Реализация очереди с помощью массива
- •2.4.2 Реализация очереди с помощью указателей
- •Глава 3. Графы
- •3.1 Основные понятия и виды графов
- •3.2 Матрица смежности
- •3.3 Список смежности
- •3.4 Список ребер
- •3.5 Матрица инцидентности
- •Глава 4. Деревья
- •4.1 Введение
- •4.2 Двоичное дерево
- •4.3 Двоичное дерево поиска
- •4.4 Куча
- •4.5.1 Балансировка
- •4.5.2 Добавление узлов
- •4.5.3 Удаление узлов
- •Раздел II алгоритмы
- •Глава 5. Анализ сложности алгоритмов
- •5.1 Введение
- •5.2 Асимптотический анализ
- •Глава 6. Сортировка
- •6.1 Сортировка пузырьком
- •6.2 Сортировка выбором
- •6.3 Сортировка вставками
- •6.4 Сортировка Шелла
- •6.5 Быстрая сортировка
- •6.5.1 Разбиение массива
- •6.5.2 Рекурсивная сортировка частей массива
- •6.6 Сортировка слиянием
- •6.7 Гномья сортировка
- •6.8 Шейкерная сортировка
- •Глава 7. Поиск
- •7.1 Линейный поиск
- •7.2 Двоичный поиск
- •7.3 Интерполяционный поиск
- •Глава 8. Теория чисел
- •8.1 Алгоритм Евклида
- •8.1.1 Метод вычитания
- •8.1.2 Метод деления
- •8.2 Бинарный алгоритм вычисления нод
- •8.3 Быстрое возведение в степень
- •8.4 Решето Эратосфена
- •8.5 Решето Сундарама
- •Глава. 9 Графы
- •9.1 Поиск в ширину
- •9.2 Поиск в глубину
- •9.3 Алгоритм Дейкстры
- •9.4 Алгоритм Флойда – Уоршелла
- •9.5 Алгоритм Беллмана — Форда
Глава 1. Массивы
1.1 Введение
Массив – это структура данных с фиксированным и упорядоченным набором однотипных элементов (компонентов). Доступ к какому-либо из элементов массива осуществляется по имени и номеру (индексу) этого элемента. Количество индексов определяет размерность массива. Так, например, чаще всего встречаются одномерные (вектора) и двумерные (матрицы) массивы. Первые имеют один индекс, вторые – два. Пусть одномерный массив называется A, тогда для получения доступа к его i-ому элементу потребуется указать название массива и номер требуемого элемента: A[i]. Когда A – матрица, то она представляема в виде таблицы, доступ к элементам которой осуществляется по имени массива, а также номерам строки и столбца, на пересечении которых расположен элемент: A[i, j], где i – номер строки, j – номер столбца.
В разных языках программирования работа с массивами может в чем-то различаться, но основные принципы, как правило, везде одни. В языке Pascal, обращение к одномерному и двумерному массиву происходит точно так, как это показано выше, а, например, в C++ двумерный массив следует указывать так: A[i][j]. Элементы массива нумеруются поочередно. На то, с какого значения начинается нумерация, влияет язык программирования. Чаще всего этим значением является 0 или 1.
Массивы, описанного типа называются статическими, но существуют также массивы по определенным признакам отличные от них: динамические и гетерогенные. Динамичность первых характеризуется непостоянностью размера, т. е. по мере выполнения программы размер динамического массива может изменяться. Такая функция делает работу с данными более гибкой, но при этом приходится жертвовать быстродействием, да и сам процесс усложняется. Обязательный критерий статического массива, как было сказано, это однородность данных, единовременно хранящихся в нем. Когда же данное условие не выполняется, то массив является гетерогенным. Его использование обусловлено недостатками, которые имеются в предыдущем виде, но оно оправданно во многих случаях.
Таким образом, даже если Вы определились со структурой, и в качестве нее выбрали массив, то этого все же недостаточно. Ведь массив это только общее обозначение, род для некоторого числа возможных реализаций. Поэтому необходимо определиться с конкретным способом представления, с наиболее подходящим массивом.
1.2 Хеш-функции
Хеш-функция – функция, преобразовывающая входную последовательность данных произвольного размера в выходную последовательность фиксированного размера. Процесс преобразования данных называется хешированием. Результат хеширования – хеш-код (хеш-сумма, хеш).
При решении класса практических задач выбирается такая хеш-функция, которая является наиболее оптимальной именно для данного класса. В общем случае следует использовать «хорошую» функцию. Когда хеш-функцию называют «хорошей», то подразумевают под этим, что она:
вычисляется достаточно быстро;
сводит к минимуму число коллизий.
Коллизией хеш-функции называется ситуация, при которой два различных элемента имеют один и тот же хеш-код. Коллизий следует избегать, выбирая «хорошую» хеш-функцию, и используя один из методов разрешения конфликтов: открытое или закрытое хеширование (см. гл. 1.3). Предотвратить коллизии могут далеко не все хеш-функции, но «хорошие» способны минимизировать вероятность их появления. При определенных обстоятельствах (известна некоторая информация о ключах), можно найти идеальную хеш-функцию, т. е. такую, которая полностью исключает возможность появления коллизий.
Использовать хеш-функцию или нет, зависит от того насколько целесообразно применение ее свойств, а также свойств алгоритма, по которому она может быть реализована на языке программирования. В одних ситуациях наиболее важна высокая скорость работы, в других равномерное распределение хеш-кодов и т. п. Далее будут рассмотрены два наиболее известных метода хеширования.
