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

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

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

Представление графа в виде матрицы смежности (adjacency matrix) -это матрица булевских значений размером Vх V, элемент которой, стоящий на пересечении v-ой строки и w-го столбца, равен 1, если в графе имеется ребро, соединяющее вершину v с вершиной w, и 0 в противном случае. Пример матрицы смежности приведен на рис. 17.8.

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

Рис. 17.8. Представление графа матрицей смежности

Данная булева матрица является еще одним представлением графа с рис. 17.1 рис. 17.1. На пересечении строки v и столбца w этой матрицы находится 1 (true), если в графе имеется ребро, соединяющее вершину v с вершиной w, и 0 (false), если такого ребра нет. Эта матрица симметрична относительно главной диагонали. Например, шестая строка (и шестой столбец) показывает, что вершина 6 соединена с вершинами 0 и 4. В некоторых случаях мы считаем, что каждая вершина соединена сама с собой, и ставим единицы на главной диагонали. Большие нулевые области в верхнем правом и нижнем левом углах матрицы обусловлены выбранной нумерацией вершин для данного примера, но не свойствами рассматриваемого графа (за исключением того, что они указывают на разреженность графа).

Программа 17.7 является реализацией интерфейса АТД графа, в которой используется непосредственное представление этой матрицы в виде вектора векторов (см. рис. 17.9). Это двумерная таблица существования, элемент adj[v][w] которой равен true, если в графе существует ребро, соединяющее вершину v с вершиной w, и false в противном случае. В случае неориентированного графа каждое ребро должно быть представлено двумя элементами: ребро v-w представлено значением true как в adj[v][w], так и в adj[w][v], что соответствует ребру w-v.

Имя DenseGRAPH в программе 17.7 подчеркивает, что эта реализация больше подходит для насыщенных, чем для разреженных графов, и отличает ее от других реализаций. Клиентская программа может воспользоваться определением типа typedef, чтобы сделать этот тип эквивалентным типу GRAPH или же явно воспользоваться именем DenseGRAPH.

В матрице смежности, которая представляет граф G, строка v есть вектор, представляющий собой таблицу существования, i-й элемент которого равно true, если вершина i смежна с v (в графе G имеется ребро v-i). Значит, чтобы дать клиентам возможность обрабатывать вершины, смежные с v, нужен код просмотра этого вектора, который находит значения true, как в программе 17.8. Понятно, что такая реализация обработки всех вершин, смежных с заданной вершиной, требует (по меньшей мере) времени, пропорционального количеству вершин V графа, независимо от количества смежных вершин.

 Структура данных для матрицы смежности

Рис. 17.9. Структура данных для матрицы смежности

На этом рисунке изображено представление графа с рис. 17.1 в виде вектора векторов языка C++.

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

Данный класс представляет собой непосредственную реализацию интерфейса из программы 17.1, основанную на представлении графа в виде вектора булевых векторов (см. рис. 17.9). Вставка и удаление ребер выполняется за постоянное время. Запросы на вставку уже существующих ребер (функция insert) молча игнорируются, хотя клиенты могут проверить наличие какого-либо ребра с помощью функции edge. Для построения графа требуется время, пропорциональное V2.

  class DenseGRAPH
    { int Vcnt, Ecnt; bool digraph;
      vector <vector <bool> > adj;
    public:
      DenseGRAPH(int V, bool digraph = false) :
        adj(V), Vcnt(V), Ecnt(0), digraph(digraph)
          { for (int i = 0; i < V; i++)
            adj[i].assign(V, false);
          }
      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;
          if (adj[v][w] == false) Ecnt++;
          adj[v][w] = true;
          if (!digraph) adj[w][v] = true;
        }
      void remove(Edge e)
        { int v = e.v, w = e.w;
          if (adj[v][w] == true) Ecnt—;
          adj[v][w] = false;
          if (!digraph) adj[w][v] = false;
        }
      bool edge(int v, int w) const
        { return adj[v][w]; }
      class adjlterator;
      friend class adjlterator;
    } ;
      

Программа 17.8. Итератор для представления матрицей смежности

Данная реализация итератора для программы 17.7 использует индекс i для пропуска элементов, равных false, в строке v матрицы смежности (adj[v]). Чтобы получить последовательность вершин, смежных с вершиной v графа G в порядке возрастания индексов вершин, необходим вызов функции beg(), а за ним последовательность вызовов функций xt() (с проверкой на false значения end() перед каждым таким вызовом).

  class DenseGRAPH::adjIterator
    { const DenseGRAPH &G;
      int i, v;
    public:
      adjIterator(const DenseGRAPH &G, int v) :
        G(G), v(v), i(-1) { }
      int beg()
        { i = -1; return nxt(); }
      int nxt()
        { for (i++; i < G.V(); i++)
          if (G.adj[v][i] == true) return i;
          return -1;
        }
      bool end()
        { return i >= G.V(); }
    };
      

Как было сказано в разделе 17.2, наш интерфейс требует, чтобы в момент инициализации графа клиенту было известно количество вершин. При необходимости можно разрешить вставку и удаление вершин (см. упражнение 17.21). Главное в конструкторе в программе 17.7 -это то, что при инициализации графа он заносит во все элементы матрицы значения false. Следует иметь в виду, что эта операция требует для своего выполнения время, пропорциональное V2, независимо от количества ребер в графе. Для краткости в программу 17.7 не включены проверки на нехватку памяти -перед использованием программы нужно добавить такие проверки (см. упражнение 17.24).

Чтобы добавить в граф ребро, в указанный элемент матрицы заносится значение true (одно для орграфов, два для неориентированных графов). Такое представление не допускает параллельных ребер: если в граф нужно вставить ребро, для которого соответствующий элемент матрицы уже равен 1, то программа ничего не изменяет. В некоторых вариантах АТД может потребоваться информировать клиент о попытке включить параллельное ребро (возможно, с помощью кода возврата функции insert). Однако петли в данном представлении возможны: ребро v-v представляется ненулевым значением элемента a[v][v].

Чтобы удалить ребро, в указанные элементы матрицы заносится значение false. При попытке удалить несуществующее ребро (для которого элементы матрицы уже равны false) ничего не изменяется. Опять-таки, в некоторых вариантах АТД лучше уведомлять клиентские программы о таких ситуациях.

При обработке больших графов, или большого количества маленьких графов, или в других случаях, когда ощущается нехватка памяти, существует несколько способов экономии памяти. Например, матрицы смежности, представляющие неориентированные графы, симметричны: a[v][w] всегда равно a[w][v]. Значит, можно сэкономить память, храня только половину симметричной матрицы (см. упражнение 17.22). Другой способ экономии значительного объема памяти заключается в использовании битовой матрицы (если этого не делает функция vector<bool>). Тогда, например, мы можем хранить представление графа, состоящего примерно из 64000 вершин в примерно 64 миллионах 64-битовых слов (см. упражнение 17.23). Эти реализации связаны с небольшим усложнением проверки существования ребра (см. упражнение 17.20). (В наших реализациях такая операция не используется, поскольку проверка, существует ли ребро v-w, сводится к проверке значения a[v][w].) Подобные методы экономии памяти эффективны, но связаны с дополнительными расходами, которые могут утяжелить внутренний цикл приложения, для которого критично время выполнения.

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

Использование матриц смежности зависит от назначения в качестве имен вершин целых чисел в диапазоне от 0 до V-1. Такое назначение можно выполнить множеством различных способов; например, в разделе 17.6 будет рассмотрена программа, которая выполняет эту процедуру. Поэтому конкретная матрица значений 0-1, которую мы представили в виде вектора векторов языка C++, не является единственно возможным представлением матрицы смежности любого заданного графа, т.к. другая программа может присвоить другие имена вершин индексам, которые мы используем для указания строк и столбцов. Две совершенно различные на первый взгляд матрицы на самом деле могут представлять один и тот же граф (см. упражнение 17.17). Это наблюдение -просто другая формулировка задачи изоморфизма графов: несмотря на необходимость определения, являются ли две различные матрицы представлением одного и того же графа, еще никто не изобрел алгоритм, который всегда мог бы эффективно решать данную задачу. Эта трудность носит фундаментальный характер. Например, наши возможности найти эффективное решение различных важных задач обработки графов полностью зависят от способа нумерации вершин (см., например, упражнение 17.26).

Программа 17.3 из раздела 17.2 выводит таблицу вершин, смежных с каждой вершиной графа. Когда она используется совместно с реализацией в программе 17.7, она выводит список вершин в порядке возрастания индексов их вершин, как на рис. 17.7. Однако учтите, что требование перебора вершин в порядке возрастания их индексов не входит в определение класса adjlterator, поэтому разработка клиента АТД, который выводит матрицу смежности, представляющую граф -нетривиальная задача (см. упражнение 17.18). Выходные данные, выводимые этими программами, сами являются представлениями графа, которые наглядно демонстрируют основные компромиссы относительно производительности алгоритма. Для вывода матрицы на странице нужно место, достаточное для размещения всех V 2 элементов; для вывода списков нужно место, достаточное для размещения V + E чисел. В случае разреженных графов, когда V2 гораздо больше, чем V + E, предпочтительнее списки, а в случае насыщенных графов, когда E и V2 сравнимы, удобнее матрица. Вскоре мы увидим необходимость такого же выбора, когда будем сравнивать представление графа матрицей смежности и его основной альтернативой -явным представлением списками смежности.

Представление графа в виде матрицы смежности неэкономично в случае разреженных графов: для построения такого представления необходимо V2 битов памяти и V2 действий. В насыщенном графе, где количество ребер (количество единичных битов в матрице) пропорционально V2, такая цена может быть приемлемой, поскольку для обработки ребер понадобится время, пропорциональное V2, независимо от выбора представления. Хотя в случае разреженного графа основной составляющей времени выполнения алгоритма может оказаться инициализация матрицы. Более того, для размещения матрицы может просто не хватить памяти. Например, мы можем столкнуться с графом, содержащим миллионы вершин и десятки миллионов ребер, и не захотим -или даже не сможем -выделить память для триллионов нулевых элементов в матрице смежности.

С другой стороны, при необходимости обработки огромного насыщенного графа нулевые элементы для обозначения отсутствующих ребер увеличивают потребность в памяти лишь на небольшой постоянный множитель, но зато позволяют определить, существует ли конкретное ребро, за постоянное время. Например, параллельные ребра автоматически запрещаются в матрицах смежности, но могут потребовать значительных затрат в других представлениях. Если в нашем распоряжении имеется достаточный объем памяти для размещения матрицы смежности, либо значение V2 настолько мало, что несущественно увеличит время обработки графа, либо мы выполняем сложный алгоритм, требующий выполнения более чем V2 действий -тогда представление в виде матрицы смежности может оказаться оптимальным выбором, независимо от насыщенности графа.

Упражнения

17.17. Приведите представления трех графов, изображенных на рис. 17.2, в виде матриц смежности.

17.18. Приведите реализацию функции show для независимого от представления графа пакета io из программы 17.4, которая выводит двумерную матрицу нулей и единиц, наподобие приведенной на рис. 17.8. Примечание', вы не должны зависеть от итератора, который перебирает вершины в порядке возрастания их индексов.

17.19. Пусть задан некоторый граф, и другой граф, идентичный первому, за исключением того, что переставлены (целочисленные) имена вершин. Как соотносятся друг с другом матрицы смежности этих двух графов?

17.20. Добавьте в АТД графа функцию edge, которая позволит клиентам проверять, существует ли ребро, соединяющие две заданные вершины, и напишите реализацию для представления графа матрицей смежности.

17.21. Добавьте в АТД графа функции, которые позволят клиентам вставлять и удалять вершины, и напишите их реализации для представления графа матрицей смежности.

17.22. Измените программу 17.7, расширенную как описано в упражнении 17.20, чтобы массив не содержал элементы a[v][w], у которых w больше, чем v -это должно снизить ее требования к памяти примерно наполовину.

17.23. Измените программу 17.7, расширенную как описано в упражнении 17.20, чтобы на компьютере со словами из B битов граф с V вершинами был представлен примерно V2/B (а не V2) словами. Эмпирически определите влияние упаковки битов в слова на время выполнения операций АТД.

17.24. Опишите, что произойдет, если при вызове конструктора из программы 17.7 не хватит памяти для размещения матрицы смежности, и внесите в код изменения, необходимые для обработки этой ситуации.

17.25. Разработайте версию программы 17.7, которая использует единственный вектор, содержащий V2 элементов.

17.26. Предположим, что имеется группа из к вершин с последовательными индексами. Как определить из матрицы смежности, образует ли эта группа вершин клику? Напишите клиентскую функцию, которая за время, пропорциональное V2, находит максимальную группу вершин с последовательными индексами, образующую клику.

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

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

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

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

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

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