Краткий ответ
Действительно, проверка того, создается ли цикл каждый раз, когда вы добавляете вершину к графу G2, стоит дорого. Более эффективный метод может заключаться в с использованием поиска в ширину (BFS) на вашем графике G1 и в сообщать о ребрах, которые вы используете для перемещения от узла к другому, когда вы путешествуете по G1 в вашем G2 по ходу дела . Я рекомендую вторую часть этого видео для демонстрации BFS.
Регулярный BFS-обход G1 для построения G2
Сначала я опишу, как стандартный обход BFS может помочь вам устранить петли в исходном графике. Ваши предпочтения по удалению одних типов ребер перед другими см. Ниже.
Я собираюсь предположить, что каждая вершина знает, каковы ее соседи (то есть каждая вершина имеет некоторый список соседей, с которыми они имеют общее ребро). Рассмотрим следующий график:
Graph1:
A------B------C
| |
D------E
При запуске алгоритма BFS мы добавим обнаруженные вершины в Graph2 с ребром, которое использовалось для их обнаружения.
Мы собираемся начать обход BFS графа в вершине A. Мы окрашиваем A как посещенный (я представляю его на графике как строчный символ) и помещаем его в нашу очередь узлов для проверки. Поскольку мы посетили A, и это наш первый узел, я поместил его в Graph2:
Graph1: Graph2:
a------B------C A
| |
D------E
Queue: A
Теперь мы выбираем первую вершину в очереди и проверяем ее соседей. Мы выбираем А из очереди. Узел A имеет "B" в качестве соседа, который еще не посещен. Мы окрашиваем B как «посещенные», добавляем его в очередь и добавляем во второй график с ребром, которое связывает его с A.
Graph1: Graph2:
a------b------C A------B
| |
D------E
Queue: A B
Следующим соседом узла A является D. D еще не посещался. Мы окрашиваем его как посещенный, добавляем в очередь и помещаем в Graph2 с краем, обозначенным буквой A. Мы исследовали всех соседей A, поэтому удалили его из нашей очереди.
Graph1: Graph2:
a------b------C A------B
| | |
d------E D
Queue: B D
Мы берем следующий узел в очереди: Узел B. Узел B имеет соседа A, который уже посещен -> здесь ничего не нужно делать. Следующий сосед - это узел C, который еще не посещен. Мы раскрасим его, добавим в очередь и добавим в graph2 с ребром в B.
Graph1: Graph2:
a------b------c A------B------C
| | |
d------E D
Queue: B D C
Последним соседом B является узел E, который еще не посещен. Мы окрашиваем его, добавляем в очередь и добавляем его ребром в узел B в graph2. Наконец, мы удаляем B из очереди, как мы закончили с этим.
Graph1: Graph2:
a------b------c A------B------C
| | | |
d------e D E
Queue: D C E
Затем мы берем следующий узел в очереди, D не имеет соседей, которые еще не посещены, поэтому он удаляется из очереди. То же самое касается узлов C и E. Когда очередь пуста, алгоритм завершается.
Теперь посмотрите на график 2: у него нет циклов.
Чтобы действительно решить вашу проблему, этот обычный обход BFS потребует некоторых адаптаций, которые я опишу ниже.
Постановка проблемы (пере) формулировка / желаемое поведение BFS для вашей конкретной потребности
Из-за ваших предпочтений в ребрах вам нужно будет немного адаптировать алгоритм, чтобы как можно больше исследовать, используя первый тип ребер, и использовать только второй тип (и третий ...), если никакая другая опция недоступна ,
Модификация состоит в том, чтобы начать исследование BFS для графа 1, но использовать только «проволочные» ребра. Когда этот первый обход прекращается (больше нет узлов в очереди), все равно останутся части графика, которые вы не смогли бы посетить, потому что вы не могли использовать ребра типа "wifi". В этом случае вы захотите перепроверить узлы, которые вы посетили, но используя ребра следующего типа. Если по Wi-Fi-ссылке вы можете получить доступ к узлу, который раньше у вас не было, вы захотите возобновить исследование с этого узла, используя «проводное» соединение.
Адаптация алгоритма BFS
Я могу придумать, как реализовать такой обход BFS. Обычно исследуемые узлы хранятся в одной очереди. После проверки всех соседей узла этот узел навсегда удаляется из очереди.
В вашей ситуации, я думаю, вы можете использовать приоритетную очередь для хранения ваших узлов. В этой приоритетной очереди есть 5 отдельных очередей: по одной для каждого типа ребер (проводной, Wi-Fi, ...). В начале исследования узел помещается в проводную очередь. Алгоритм DFS исследует этот узел и добавит соседей, к которым можно получить доступ по проводам, в «проводную» очередь. Затем, вместо того, чтобы отбрасывать этот первый узел, вы помещаете его в следующую очередь, а именно в очередь "wifi".
Алгоритм BFS всегда должен выбирать узлы в очереди предпочитаемых ребер. Только когда нет узлов в «проводной очереди», он выбирает в очереди «Wifi».
Еще одна тонкость: когда вы выбираете узел из очереди "wifi", если есть невидимый сосед этого узла по wifi, вы помещаете этого соседа в очередь "проводной" и перезапускаете BFS, используя этот узел. В этой конкретной ситуации, когда вы получаете доступ к невидимому узлу с фронта с «более низким приоритетом», вы должны прекратить проверять соседей вашего текущего узла, так как эти соседи могут быть доступны по проводам от первого найденного вами соседа. Поэтому вы оставите текущий узел в своей очереди, чтобы вернуться к нему позже.
Надеюсь, мои объяснения понятны, я с нетерпением жду ваших комментариев.