- •Содержание
- •Глава 5. Алгоритмы сортировок 52
- •Глава 6. Алгоритмы поиска 63
- •Глава 1. Основные операции при работе с деревьями
- •1.1. Определение глубины дерева
- •1.2. Операции поиска и включения элемента в дерево
- •1.3. Оптимизация поиска в дереве
- •1.3.1. Обходы дерева с нумерацией вершин
- •1.4. Поиск и включение в двоичное дерево
- •Контрольные вопросы
- •Задания для практической работы
- •Глава 2. Сбалансированные двоичные деревья
- •2.1. Преобразование двоичного дерева в лозу.
- •2.2. Преобразование лозы в сбалансированное двоичное дерево.
- •Контрольные вопросы
- •Задания для практической работы
- •Глава 3. Жадные алгоритмы
- •3.1. Понятие жадного алгоритма
- •3.2. Алгоритм Хаффмана
- •Контрольные вопросы
- •Задания для практической работы
- •Глава 4. Графы
- •4.1. Алгоритмы представления графа
- •4.1.1. Представление графа в виде массива
- •4.1.2. Представление графа в виде матрицы смежности
- •4.1.3. Представление графа в виде связанного списка
- •4.1.4. Представление графа в виде списка дуг
- •4.1.5. Преобразования структур графа
- •4.2. Обходы в графах
- •4.3. Определение путей и контуров Эйлера
- •4.4. Поиск кратчайших путей
- •4.4.1. Алгоритм э. Дейкстры.
- •4.4.2. Алгоритм Флойда — Уоршалла
- •4.5. Определение остовных деревьев
- •4.5.1. Алгоритм Крускала
- •4.5.2. Алгоритм Прима
- •Контрольные вопросы
- •Определение путей и контуров Эйлера
- •Задания для практической работы
- •Глава 5. Алгоритмы сортировок
- •5.1. Сортировка выбором
- •5.2. Сортировка вставками
- •5.3. Пузырьковая сортировка
- •5.4. Быстрая сортировка
- •5.5. Сортировка слиянием
- •5.6. Пирамидальная сортировка
- •Контрольные вопросы
- •Задания для практической работы
- •Глава 6. Алгоритмы поиска
- •6.1. Последовательный поиск
- •6.2. Двоичный поиск
- •6.3. Работа со словарем. Иоиск и вставка. Хеширование.
- •6.4. Поиск подстроки в строке
- •6.4.1. Алгоритм прямого поиска подстроки в строке
- •Контрольные вопросы
- •Задания для практической работы
- •Литература
4.1.4. Представление графа в виде списка дуг
Иногда используются и другие представления графов, например, для случая очень разреженных графов, когда при большом количестве N вершин графа число дуг существенно меньше NXN, например, порядка N. Одним из таких представлений является представление в виде списка дуг, когда все дуги графа собраны в единый список, в каждом элементе которого записана информация об обоих концах дуги, а также, возможно, о нагрузке на дугу (рис.3).
Рис.3. Граф и его представление в виде списка дуг
В листинге приведено представление графа в виде списка дуг. Такое представление будем называть A-графом от слова Arc— дуга. Список дуг представлен списком элементов, каждый из которых содержит два целых числа— номера концов дуги. В данной реализации список не представлен в виде отдельного описания класса. Вместо этого описание элемента такого списка— дуги— внесено прямо в описание класса для представления A-графа, а реализации операций над списком просто являются частью реализации операций над дугами графа.
Операция добавления дуги в этом представлении — это просто операция добавления одного элемента в конец списка, и реализуется она сравнительно эффективно. Напротив, при выполнении операции поиска дуги может потребоваться просмотреть весь список. Разумеется, настолько же неэффективной будет и операция удаления дуги.
файл ArcGraph.h
#include "graph.h" // Определение родительского класса
// Описание класса для представления A-графа
class ArcGraph : public Graph {
// Дуга представлена элементом списка, содержащим номера
// концов дуги и указатель на следующий элемент списка
struct Arc {
int begin, end;
Arc *next;
// Конструктор дуги.
Arc{int b, int e, Arc *n = NULL) {
begin = b; end = e; next = n; )
};
// Список дуг представлен, как обычно,
// указателями на первый и последний элементы списка
Arc *first, *last;
// arcCount - счетчик числа дуг-элементов списка
int arcCount;
// vertexNumber - количество вершин графа, используемое
//в данном представлении только для контроля номеров вершин
int vertexNumber;
public:
// Конструктор графа инициализирует пустой список
// и запоминает количество вершин графа.
ArcGraph(int n) {
first = last = NULL; arcCount = 0; vertexNumber = n;
}
// Функция подсчета количества вершин выдает запомненное значение
int vertexCoun() const { return vertexNumber; }
// Операции над дугами:
void addArc(int from, int to);
bool hasArc(int from, int to) const;
};
файл ArcGraph.cpp
«include "ArcGraph.h"
//Реализация операции добавления дуги
void ArcGraph::addArc(int from, int to) {
// Сначала проверяем правильность задания номеров вершин.
if (from < 0 || to < 0 || from >= vertexNumber || to >=vertexNumber)
return;
Arc *newArc = new Arc(from, to);
// Новая дуга добавляется в конец списка
if (last) last->next = newArc; else first = newArc;
last = newArc;
arcCount++;
}
// Реализация операции проверки наличия дуги.
bool ArcGraph::hasArc(int from, int to) const {
// Необходимо просмотреть список дуг в поисках нужной.
for (Arc *current = first; current; current = current->next) {
if (current->begin == from && current->end == to)
return true;
}
return false;
}
Конечно, выбирать представление графа для решения конкретной задачи нужно исходя из того, какие операции с графами будут выполняться чаще всего. Если в процессе работы граф часто меняет структуру связей, то, наверное, лучшим способом представления будет матрица смежности. Если наиболее частой операцией будет поиск путей в графе, проходящих по направлению дуг, то более удобным и выгодным способом будет способ представления графа в виде списков смежности.
Однако иногда бывает, что выбрать какой-то один способ представления графа трудно, например, потому, что в разные периоды времени работы программы требуется выполнять различные операции. В этом случае было бы удобно переходить от одного представления графа к другому. К счастью, такие преобразования занимают не очень много времени. Конечно, для преобразования придется, по крайней мере, один раз просмотреть весь граф, но время, затраченное на построение нового представления, всегда будет прямо пропорционально размеру графа. Это означает, что если время работы некоторого алгоритма на графе имеет порядок сложности не меньше размера графа, то скорость его работы практически не зависит от способа представления графа, поскольку всегда можно перед началом работы выполнить нужное преобразование без существенной потери эффективности основного алгоритма.