
- •Основы алгоритмизации и программирования, язык Си
- •Введение
- •Блок-схема алгоритма Общие требования к блок-схеме алгоритма
- •Линейные и разветвляющиеся процессы
- •Циклические процессы
- •Итерационные процессы
- •Комментарии
- •Типы данных
- •Данные целого типа
- •Данные вещественного типа
- •Модификатор const
- •Переменные перечисляемого типа
- •Константы
- •Операции и выражения
- •Операция присваивания
- •Арифметические операции
- •Операции поразрядной арифметики
- •Логические операции
- •Операции отношения
- •Инкрементные и декрементные операции
- •Операция sizeof
- •Порядок выполнения операций
- •Приоритет операций
- •Преобразование типов
- •Операция приведения
- •Операция запятая
- •Ввод и вывод информации
- •Директивы препроцессора Директива #include
- •Директива #define
- •Понятие пустого и составного операторов
- •Условные операторы
- •Операторы организации цикла
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла do … while
- •Вложенные циклы
- •Операторы перехода (break, continue, return, goto)
- •Примеры программ
- •Массивы Одномерные массивы
- •Примеры программ
- •Многомерные массивы (матрицы)
- •Примеры программ
- •Указатели Понятие указателя
- •Описание указателей
- •Операции с указателями
- •Связь между указателями и массивами
- •Массивы указателей
- •Многоуровневые указатели
- •Примеры программ
- •Символьные строки
- •Ввод/вывод строк.
- •Функции работы со строками.
- •Примеры программ
- •Функции
- •Прототип функции.
- •Определение функции.
- •Параметры функции
- •Параметры по умолчанию
- •Передача массива в функцию
- •Inline функции
- •Класс памяти
- •Автоматические переменные
- •Статические переменные
- •Регистровые переменные
- •Блочная структура
- •Примеры программ
- •Указатели на функции
- •Примеры программ
- •Рекурсия
- •Примеры программ
- •Аргументы в командной строке
- •Функции с переменным числом параметров
- •Примеры программ
- •Сортировка
- •Пузырьковая сортировка.
- •Шейкер сортировка
- •Сортировка вставкой
- •Сортировка выбором
- •Метод Шелла
- •Метод Хора
- •Структуры
- •Доступ к элементам структуры
- •Инициализация структур
- •Указатели на структуры.
- •Структуры и функции
- •Примеры программ
- •Поля бит
- •Объединения
- •Переменные с изменяемой структурой
- •Примеры программ
- •Организация списков и их обработка
- •Операции со списками при связном хранении
- •Построение обратной польской записи
- •Односвязный линейный список, очередь
- •Двусвязный линейный список
- •Циклический список, кольцо
- •Двусвязный циклический список
- •Примеры программ
- •Деревья
- •Потоки и файлы
- •Файлы Основные сведения о файловой системе
- •Организация посимвольного ввода и вывода
- •Определение конца файла feof()
- •Организация ввода и вывода строк
- •Удаление файлов
- •Дозапись потока
- •Позиционирование в файле
- •Текстовые и двоичные файлы
- •Функции fread() и fwrite()
- •Примеры программ
- •Хеширование
- •Схемы хеширования
- •Метод открытой адресации с линейным опробыванием
- •Метод цепочек
- •Машинное представление графов
- •Примеры программ
- •Литература
Схемы хеширования
В большинстве задач два и более ключей хешируются одинаково, но они не могут занимать в хеш-таблице одну и ту же ячейку.
Существуют два возможных варианта: либо найти для нового ключа другую позицию, либо создать для каждого индекса хеш-таблицы отдельный список, в который помещаются все ключи, отображающиеся в этот индекс.
Эти варианты и представляют собой две классические схемы хеширования:
- хеширование методом открытой адресации с линейным опробыванием;
- хеширование методом цепочек (со списками), или так называемое, многомерное хеширование.
Метод открытой адресации с линейным опробыванием
Изначально все ячейки хеш-таблицы, которая является обычным одномерным массивом, помечены как не занятые. Поэтому при добавлении нового ключа проверяется, занята ли данная ячейка.
Если ячейка занята, то осуществляется осмотр по кругу до тех пор, пока не найдется свободное место («открытый адрес»), т.е. элементы с однородными ключами размещают вблизи полученного индекса.
В дальнейшем, осуществляя поиск, сначала находят по ключу позицию i в таблице, и, если ключ не совпадает, то последующий поиск осуществляется в соответствии с алгоритмом разрешения конфликтов, начиная с позиции i по списку.
Метод цепочек
Этот метод является доминирующей стратегией. В этом случае i, полученной из выбранной хеш-функцией h(key)=i, трактуется как индекс в хеш-таблице списков, т.е. сначала ключ key очередной записи отображается на позицию i = h(key) таблицы. Если позиция свободна, то в нее размещается элемент с ключом key, если же она занята, то отрабатывается алгоритм разрешения конфликтов, в результате которого такие ключи помещаются в список, начинающийся в i-той ячейке хеш-таблицы.
В С Т А В И Т Ь Р И С У Н О К
В итоге имеем таблицу массива связных списков или деревьев.
Процесс заполнения (считывания) хеш-таблицы прост, но доступ к элементам требует выполнения следующих операций:
- вычисление индекса i;
- поиск в соответствующей цепочке.
Для улучшения поиска при добавлении нового элемента можно использовать алгоритма вставки не в конец списка, а с упорядочиванием, т.е. добавлять элемент в нужное место.
Пример реализации метода прямой адресации с линейным опробыванием. Исходными данными являются 7 записей (для простоты информационная часть состоит только из целочисленных данных), объявленного структурного типа:
struct zap
{ int key; // Ключ
int info; // Информация
} data;
{59,1}, {70,3}, {96,5}, {81,7}, {13,8}, {41,2}, {79,9}; размер хеш-таблицы m = 10.
Хеш-функция i = h(data) = data.key % 10; т.е. остаток от деления на 10 - i Î [0,9].
На основании исходных данных последовательно заполняем хеш-таблицу.
Хеш-таблица (m=10)
0
1
2
3
4
5
6
7
8
9
70
81
41
13
79
96
59
3
7
2
8
9
5
1
1
1
2
1
6
1
1
Хеш-адреса i
:
key
информация
Хеширование первых пяти ключей дает различные индексы (хеш-адреса):
i = 59 % 10 = 9; i = 70 % 10 = 0; i = 96 % 10 = 6;
i = 81 % 10 = 1; i = 13 % 10 = 3.
Первая коллизия возникает между ключами 81 и 41 - место с индексом 1 занято. Поэтому просматриваем хеш-таблицу с целью поиска ближайшего свободного места, в данном случае - это i = 2.
Следующий ключ 79 также порождает коллизию: позиция 9 уже занята. Эффективность алгоритма резко падает, т.к. для поиска свободного места понадобилось 6 проб (сравнений), свободным оказался индекс i = 4.
Общее число проб такого метода от 1 до n - 1 пробы на элемент, где n - размер хеш-таблицы.
Реализация метода цепочек для предыдущего примера. Объявляем структурный тип для элемента списка (однонаправленного):
struct zap
{ int key; // Ключ
int info; // Информация
zap *Next; // Указатель на следующий
} data;
На основании исходных данных последовательно заполняем хеш-таблицу, добавляя новый элемент в конец списка, если место уже занято.
Хеш-таблица (m=10)
0
1
2
3
4
5
6
7
8
9
70
81
13
96
59
3
7
8
5
1
NULL
NULL
NULL
41
79
2
9
NULL
NULL
Хеширование первых пяти ключей, как и в предыдущем случае, дает различные индексы (хеш-адреса): 9, 0, 6, 1, и 3.
При возникновении коллизии, новый элемент добавляется в конец списка. Поэтому элемент с ключом 41, помещается после элемента с ключом 81, а элемент с ключом 79 - после элемента с ключом 59.
Графы
Граф – это сложная нелинейная многосвязная динамическая структура, отображающая свойства и связи сложного объекта
Многосвязная структура обладает следующими свойствами:
- на каждый элемент (узел, вершину) может быть произвольное число ссылок;
- каждый элемент может иметь связь с любым количеством других элементов;
- каждая связка (ребро, дуга) может иметь направление и вес.
Вершины графа содержат информацию об элементах объекта. Связи между вершинами задаются ребрами графа. Ребра, изображаемые стрелками – ориентированные, иначе нет. Граф, все ребра которого ориентированы, называется ориентированным графом или орграфом. Обозначение связей: неориентированных (А,В), ориентированных <А,В>.
Для ориентированного графа число ребер, входящих в узел, называется полустепенью захода узла, а выходящих из узла – полустепенью исхода. Граф без ребер – нуль-граф.
Узел A называется инцидентным дуге х, если А – это один из двух узлов упорядоченной пары, составляющей дугу х.
Граф называется взвешенным, если его ребрам поставлены в соответствие некоторые значения.
Путь в графе – это последовательность узлов, связанных ребрами. Элементарным называется путь в котором все ребра различны, простым называется путь в котором все вершины различны. Путь от узла к самому себе называется циклом, а содержащий его граф – циклическим.
Два узла смежны, если существует путь из одного из них в другой.