Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2178 / 0 | Длительность: 63:16:00
Лекция 17:

Виды графов и их свойства

Представление графа в виде списков смежности

Стандартное представление графа, которое обычно выбирают для ненасыщенных графов, называется представлением списками смежности (adjacency lists).

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

Программа 17.9 представляет собой реализацию интерфейса АТД из программы 17.1, основанную на данном подходе, а на рис. 17.10 приведен соответствующий пример. Чтобы добавить в это представление графа ребро, соединяющее вершины v и w, мы добавляем w в список смежности вершины v, а v -в список смежности вершины w. Таким образом, добавление новых ребер выполняется за постоянное время, однако общий объем занимаемой при этом памяти пропорционален сумме количества вершин и количества ребер (в отличие от пропорциональности квадрату количества вершин для представления графа матрицей смежности). Ребра неориентированных графов опять фигурируют в двух различных местах, т.к. ребро, соединяющее вершину v с w, представлено узлами в обоих списках смежности. Оба включения обязательны, иначе мы не сможем эффективно отвечать на простые вопросы вроде " Какие вершины смежны с вершиной v? " Программа 17.10 реализует итератор, который дает ответ клиентам, задающим такие вопросы, за время, пропорциональное количеству таких вершин.

 Структура данных списков смежности

Рис. 17.10. Структура данных списков смежности

Здесь показано представление графа с рис. 17.1 в виде массива связных списков. Объем используемой памяти пропорционален сумме количества вершин и количества ребер. Чтобы найти индексы вершин, связанных с заданной вершиной v, мы выбираем v-ю позицию этого массива, которая содержит указатель на связный список, содержащий по одному узлу для каждой вершины, связанной с v. Порядок, в котором узлы расположены в списках, зависит от метода, которым были построены эти списки.

Реализации в программах 17.9 и 17.10 являются низкоуровневыми. В качестве альтернативы можно реализовать каждый связный список с помощью контейнера list из библиотеки STL (см. упражнение 17.30). Недостаток такого подхода заключается в том, что реализациям list из STL приходится поддерживать гораздо большее количество операций, чем нужно нам, а это обычно означает излишние затраты, которые могут ухудшить производительность всех разрабатываемых нами алгоритмов (см. упражнение 17.31). Вообще-то все наши алгоритмы обработки графов используют интерфейс АТД Graph, так что эта реализация -вполне подходящее место для инкапсуляции всех низкоуровневых операций. Так мы достигнем нужной эффективности, не затрагивая другие программы. Другое преимущество представления графа связными списками заключается в том, что оно обеспечивает базу для оценки производительности наших приложений. Однако учтите, что реализация связных списков в программах 17.9 и 17.10 неполна, в ней нет деструктора и конструктора копирования. Во многих случаях это может привести к неожиданным результатам или серьезному снижению производительности. Эти функции являются прямыми расширениями функций из реализации очереди первого класса в программе 4.22 (см. упражнение 17.29).

Программа 17.9. Реализация АТД графа (списками смежности)

Данная реализация интерфейса из программы 17.1 использует вектор связных списков, каждый из которых соответствует одной вершине. Она эквивалентна представлению в программе 3.15, где ребро v-w представлено узлом вершины w в списке вершины v и узлом вершины v в списке вершины w.

Реализации функций remove и edge, а также конструктора копирования и деструктора оставлены в качестве самостоятельных упражнений. Код функции insert обеспечивает постоянное время вставки ребра за счет отказа от проверки параллельности ребер. Общий объем используемой памяти пропорционален V + E, т.е. это представление больше подходит для разреженных мультиграфов.

Клиентские программы могут воспользоваться конструкцией typedef, чтобы сделать этот тип эквивалентным типу GRAPH, или явно использовать класс SparceMultiGRAPH.

  class SparseMultiGRAPH
    { int Vcnt, Ecnt; bool digraph;
      struct node
        { int v; node* next;
          node(int x, node* t) { v = x; next = t; }
        } ;
      typedef node* link;
      vector <link> adj;
    public:
      SparseMultiGRAPH(int V, bool digraph = false) :
        adj(V), Vcnt(V), Ecnt(0), digraph(digraph)
        { adj.assign(V, 0); }
      int V() const { return Vcnt; }
      int E() const { return Ecnt; }
      bool directed() const { return digraph; }
      void insert(Edge e)
        { int v = e.v, w = e.w;
          adj[v] = new node(w, adj[v]);
          if (!digraph) adj[w] = new node(v, adj[w]);
          Ecnt++;
        }
      void remove(Edge e);
      bool edge(int v, int w) const;
      class adjlterator;
      friend class adjlterator;
      } ;
      

В данной книге мы полагаем, что объекты SparceMultiGRAPH содержат их. В этом смысле STL-контейнер list гораздо удобнее низкоуровневых однонаправленных списков, он снимает необходимость в дополнительном кодировании, поскольку соответствующий деструктор и конструктор копирования определены автоматически. Например, объекты DenseGRAPH, построенные в программе 17.7, правильно уничтожаются и копируются клиентскими программами, так как они построены из объектов библиотеки STL.

В отличие от программы 17.7, программа 17.9 строит мультиграфы, т.к. она не удаляет параллельные ребра. Для выявления повторяющихся ребер в структуре списков смежности необходим просмотр списков со временем, пропорциональным V. Кроме того, в программе 17.9 отсутствует реализация операции удалить ребро и проверить наличие ребра. Добавление реализаций этих функций не представляет труда (см. упражнение 17.28), но каждая такая операция может потребовать время, пропорциональное V, на поиск в списках узлов, представляющих ребра. Из-за этих затрат представление списками смежности может оказаться неприемлемым для приложений, выполняющих обработку очень больших графов, в которых недопустимы параллельные ребра, или для приложений, в которых интенсивно используются операции удалить ребро или проверить наличие ребра. В разделе 17.5 будут рассмотрены реализации списками смежности, которые обеспечивают выполнение операций удалить ребро и проверить наличие ребра за постоянное время.

Если в качестве имен вершин графа используются обозначения, отличные от целых чисел, то (как и в случае матриц смежности) две разные программы могут связать имена вершин с целыми числами в диапазоне от 0 до V-1 двумя различными способами, а это приведет к образованию двух различных структур списков смежности (см., например, программу 17.15). Из-за сложности задачи изоморфизма графов трудно рассчитывать на то, что мы сумеем определить, представляют ли различные структуры один и тот же граф.

Программа 17.10. Итератор для представления списками смежности

Данная реализация итератора для программы 17.9 использует ссылку t для обхода связного списка, присоединенного к вершине v. Чтобы получить последовательность вершин, смежных с вершиной v графа G, необходим вызов функции beg() , а за ним последовательность вызовов функций nxt() (с проверкой на false значения end() перед каждым таким вызовом).

  class SparseMultiGRAPH::adjIterator
    { const SparseMultiGRAPH &G;
      int v;
      link t;
    public:
      adjIterator(const SparseMultiGRAPH &G, int v) :
        G(G), v(v) { t = 0; }
      int beg()
        { t = G.adj[v]; return t ? t->v : -1; }
      int nxt()
        { if (t) t = t->next; return t ? t->v : -1; }
      bool end()
        { return t == 0; }
    };
      

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

Основное преимущество представления списками смежности по сравнению с представлением матрицей смежности заключается в том, что оно всегда требует объем памяти, пропорциональный V + E, а не V2. А основной недостаток состоит в том, что проверка наличия конкретных ребер может потребовать время, пропорциональное V -в отличие от постоянного времени для матрицы смежности. Эти различия, по сути, возникают из-за различия в использовании связных списков и векторов для представления множеств вершин, инцидентных каждой вершине.

Итак, мы снова приходим к тому, что понимание базовых свойств связных структур данных и векторов критично для построения эффективных реализаций АТД графа. Интерес к такому различию в производительности естественен, т.к. мы хотим избежать слишком неэффективных реализаций, когда от АТД требуется выполнение множества разнообразных операций. В разделе 17.5 будут рассмотрены вопросы применения базовых структур данных для использования возможных достоинств обеих рассматриваемых структур на практике. И все-таки простая реализация, приведенная в программе 17.9, обладает всеми свойствами, которые необходимы для изучения эффективных алгоритмов обработки разреженных графов.

Упражнения

17.27. В стиле рисунка 17.10 приведите структуру списков смежности, полученную при вставке программой 17.9 ребер

3-71-47-80-55-23-82-90-64-92-66-4(вуказанно мпо рядке)вперво начально пусто йграф.

17.28. Напишите реализации функций remove и edge для класса графов, представленных списками смежности (программа 17.9). Примечание', в случае параллельных ребер достаточно удалить любое ребро, соединяющее заданные вершины.

17.29. Добавьте конструктор копирования и деструктор в класс графов, представленных списками смежности (программа 17.9). Совет, см. программу 4.22.

17.30. Измените реализацию класса SparceMultiGRAPH в программах 17.9 и 17.10, используя вместо связного списка STL-контейнер list для представления каждого списка смежности.

17.31. Эмпирически сравните реализацию класса SparceMultiGRAPH из упражнения 17.30 с реализацией, приведенной в тексте. На примере специально подобранного множества значений V сравните значения времени работы клиентской программы, которая строит полные графы с V вершинами, после чего перебирает их ребра с помощью программы 17.2.

17.32. Приведите простой пример представления графа списками смежности, которое невозможно построить многократной вставкой ребер с помощью программы 17.9.

17.33. Сколько различных представлений графа в виде списков смежности представляют один и тот же граф, показанный на рис. 17.10 рис. 17.10?

17.34. Добавьте в АТД графа (программа 17.1) объявление общедоступной функции-члена, которая удаляет петли и параллельные ребра. Напишите тривиальную реализацию этой функции для класса на базе матрицы смежности (программа 17.7) и для класса на базе списков смежности (программа 17.9), которая использует время, пропорциональное E, и объем памяти, пропорциональный V.

17.35. Напишите версию программы 17.9, которая блокирует добавление параллельных ребер (просматривая список смежности при каждой вставке ребра) и петель. Сравните полученную реализацию с реализацией из упражнения 17.34. Какая из них лучше подходит для работы со статическими графами? Примечание, оптимальную реализацию см. в упражнении 17.49.

17.36. Напишите клиентскую программу АТД графа, которая возвращает результат удаления петель, параллельных ребер и вершин степени 0 (изолированных вершин) из заданного графа. Примечание, Время выполнения вашей программы должно линейно зависеть от размера представления графа.

17.37. Напишите клиентскую программу АТД графа, которая возвращает результат удаления из заданного графа петель и сворачиваемых путей, т.е. состоящих исключительно из вершин степени 2. А именно, каждая вершина степени 2 в графе без параллельных ребер принадлежит некоторому пути u-...-w, где вершины u и w могут иметь степени, не равные 2. Замените каждый такой путь ребром u-w, а затем удалите все промежуточные вершины степени 2 как в упражнении 17.37. Примечание, Эта операция может привести к появлению петель и параллельных ребер, но она сохраняет степени вершин, которые не были удалены.

17.38. Приведите (мульти)граф, полученный преобразованием из упражнения 17.37 графа, показанного на рис. 17.1.

Бактыгуль Асаинова
Бактыгуль Асаинова

Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат?

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?