![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Топологическая сортировка (с переименованием)
Пусть задан произвольный DAG (вверху), топологическая сортировка позволяет нам переименовать его вершины так, что каждое ребро ведет из вершины с меньшим номером в вершину с большим номером (внизу). В этом примере мы переименовываем вершины 4, 5, 7 и 8, соответственно, в 7, 8, 5 и 4, как показано в массиве tsI Существует большое число переименований, позволяющих получить требуемые результаты. Часть 5. Алгоритмы на графах
РИСУНОК 19.22. ТОПОЛОГИЧЕСКАЯ СОРТИРОВКА (С ПЕРЕГРУППИРОВКОЙ) Эта диаграмма может служить другой точкой зрения на топологическую сортировку, представленную на рис. 19.21; в ней мы определяем способ перегруппировки вершин, а не их переименования. Когда мы группируем вершины в порядке, указанном в массиве is, слева направо, то все ориентированные ребра направлены слева направо. Инверсия перестановки ts есть перестановка tsl, которая определяет переименование, описанное на рис. 19.21. for (i = 0; i < V; i++) tsI[ts[i]] = i; определяет переименование в векторе tsl, индексированном именами вершин. И наоборот, мы можем получить перегруппировку из переименования с помощью цикла for (i = 0; i < V; i++) ts[tsI[i]] = i; который помещает первой в список вершину, получающую метку 0, второй вершину, получающую метку 1, и т.д. Чаще всего мы используем термин топологическая сортировка (topological sort) в отношении версии задачи, связанной с перегруппировкой. Обратите внимание на то обстоятельство, что ts не есть вектор, индексированный именами вершин. В общем случае, порядок вершин, установленный топологической сортировкой, не уникален. Например, суть логические сортировки примера DAG, представленного на рис. 19.6 (существует множество других примеров). В приложении составления расписаний подобная ситуация возникает всякий раз, когда одна из задач прямо или косвенно не находится в прямой или непрямой зависимости от другой задачи, и в силу этого обстоятельства она может выполняться перед либо после другой задачи (и даже одновременно). Число возможных расписаний возрастает экспоненциально с увеличением количества таких пар задач. Как уже отмечалось, иногда бывает полезно рассматривать ребра орграфа в другом аспекте: когда мы говорим, что ребро направлено из s в t, это означает, что вершина s " зависит" от вершины tНапример, вершины могут представлять термины, определения которых даются в некоторой книге, при этом ребро направлено из s в t, если определение s использует tВ этом случае полезно установить порядок, по условиям которого определение каждого термина дается перед тем, как оно будет использоваться в другом определении. Использование подобной упорядоченности соответствует такому построению вершин в линию, что все ребра направлены справа налево — получаем обратную топологическую сортировку (reverse topological sort). Рисунок 19, 23 может служить иллюстрацией обратной топологической сортировки на рассматриваемом нами демонстрационном примере DAG. Глава 19. Орграфы и ориентированные ациклические графы
РИСУНОК 19.23. ОБРАТНАЯ ТОПОЛОГИЧЕСКАЯ СОРТИРОВКА В условиях обратной топологической сортировки, выполняемой на демонстрационном примере орграфа, все ребра направлены справа налево. Нумерация вершин, задаваемая обратной подстановкой tsI, порождает граф, в котором каждое ребро направлено из вершины с большим номером в вершину с меньшим номером.
Теперь выясняется, что мы уже рассматривали алгоритм обратной топологической сортировки, таковым является наш старый знакомый — стандартный рекурсивный поиск в глубину! Если на вход подать DAG, то нумерация вершин во время обхода в обратном порядке размещает вершины в порядке обратной топологии. Иначе говоря, мы выполняем нумерацию каждой вершины как завершающее действие рекурсивной функции DFS аналогично вектору post в программе 19.2, которая представляет собой реализацию поиска в глубину (DFS). Как видно из рис. 19.24, использование такой нумерации эквивалентно нумерации узлов в лесе DFS при обходе в обратном порядке и эквивалентно топологической сортировке: вектор post, индексированный по именам вершин, позволяет получить переименование и его инверсию, показанные на рис. 19.23, — получается обратная топологическая сортировка графа DAG. Свойство 19.11. Нумерация при обходе в обратном порядке при поиске в глубину представляет собой обратную топологическую сортировку для любого графа DAG. Доказательство: Предположим, что s и t — две вершины, такие, что s появляется раньше t при нумерации в обратном порядке, даже если в РИСУНОК 19.24. ЛЕС DFS ДЛЯ ГРАФА DAG Лес DFS заданного орграфа не имеет обратных ребер (ребер, ведущих в узлы с большими номерами в обратном порядке обхода вершин графа) тогда и только тогда, когда этот орграф есть DAG. Ребра, не принадлежащие деревьям в этом DFS лесе для графа DAG, представленного на рис. 19.21, — это либо прямые ребра (заштрихованные квадратики), либо поперечные ребра (незаштрихованные квадратики). Последовательность, в которой посещаются вершины при обходе леса в обратном порядке, показанная в нижней части диаграммы, представляет собой обратную топологическую сортировку (см. рис. 19.23). Часть 5. Алгоритмы на графах
Таким образом, мы легко можем заставить стандартный поиск в глубину выполнять топологическую сортировку, о чем свидетельствует программа 19.6. Эта программа выполняет обратную топологическую сортировку: она выполняет перестановку во время обхода в обратном порядке и ее инверсию, благодаря чему клиентские программы получают возможность переименовать или перегруппировать вершины. Программа 19.6. Обратная топологическая сортировка_____________________________ Этот класс DFS вычисляет нумерацию леса DFS (обратная топологическая сортировка). Клиентские программы могут использовать объект TS для переименования вершин графа DAG с таким расчетом, чтобы каждое ребро указывало из вершины с некоторым номером на вершину с более низким номером или чтобы вершины были упорядочены в такой последовательности, что исходная вершина каждого ребра появлялась после вершины назначения (см. рис. 19.23). template < class Dag> class dagTS { const Dag & D; int cnt, tсnt; vector< int> pre, post, postI; void tsR(int v) { pre[v] =cnt++; typename Dag:: adjIterator A(D, v); for (int t - A.beg();! A.end(); t = A.nxt()) if < pre[t] == -1) tsR(t); post[v] «tcnt; postI[tcnt++] ■ v; } public: dagTS(const Dag & D): D(D), tcnt(O), cnt(O), pre(D.V(), -1), post(D.V(), -1), postI(D.V(), -1) { for (int v = 0; v < D.V(); v++) if (pre[v] == -1) tsR(v); } int operator[](int v) const { return postl[v]; } int relabel(int v) const { return post[v]; } };
■ Выполнить обратную топологическую сортировку на обращении заданного графа ■ Вместо того чтобы использовать номер вершины в качестве индекса при нумера ■ Пронумеруйте вершины в обратном порядке (начните с V— 1 и выполните отсчет Глава 19. Орграфы и ориентированные ациклические графы
Чтобы реализовать первый из вариантов, перечисленных в предыдущем параграфе в контексте разреженных графов (представленных в виде списков смежных вершин), нам может потребоваться программа 19.1 для вычисления обратного графа. Решение этой задачи требует увеличения используемого пространства памяти в два раза, что становится обременительным в случае крупных графов. Что касается насыщенных графов (представленных в виде матриц смежности), то как было отмечено в разделе 19.1, мы можем выполнить поиск в глубину на обратном графе, не прибегая к использованию дополнительного пространства памяти или без выполнения дополнительных работ, а только путем простой замены строк на столбцы при обращении к матрице смежности, как показывает программа 19.7. Далее мы рассмотрим альтернативный классический метод топологической сортировки, которые имеет определенное сходство с BFS (breadth-first search — поиск в ширину) (см. раздел 18.7.). Он основан на следующем свойстве графа DAG. Свойство 19.12. У каждого графа DAG имеется, по меньшей мере, один исток и, по меньшей мере, один сток. Доказательство: Предположим, что задан DAG, у которого нет стоков. Тогда, отправляясь из любой вершины, мы можем построить ориентированный путь произвольной длины, следуя из этой вершины вдоль любого ребра в любую другую вершину (существует, по меньшей мере, одно такое ребро, поскольку у графа DAG нет стоков), с последующим следованием из этой вершины вдоль другого ребра и т.д. Но как только мы посетим V+ 1 вершину, мы должны попасть в ориентированный цикл в соответствии с принципом картотечного ящика (см. свойство 19.6), что противоречит предположению о том, что имеется DAG. Таким образом, что в графе DAG существует, по меньшей мере, один сток. Отсюда также следует, что в каждом графе DAG присутствует, по меньшей мере, один исток, ибо исток представляет собой обращение стока. ■ Из этого факта мы можем вывести алгоритм топологической сортировки: пометим любой исток наименьшей неиспользованной меткой, затем удалим его и пометим остальную часть графа DAG, применив тем же алгоритм. На рис. 19.25 показана трассировка этого алгоритма, примененного к рассматриваемому нами примеру графа DAG. Программа 19.7. Топологическая сортировка___________________________________________ Если использовать данную реализацию функции tsR из программы 19.6, конструктор вычисляет топологическую сортировку, но не ее обращение (для любого DAG это реализация, которая поддерживает функцию edge), поскольку она замещает вызов функции edge(v, w) при поиске в глубину на вызов edge(w, v), тем самым выполняя обработку обратного графа (см. текст).
|