检测在线图形中连续添加边缘的周期?

时间:2016-12-28 03:38:56

标签: java algorithm graph

如何在有向图中检测一个循环,其中一个接一个地添加边。 如果添加最新边缘导致循环创建,则不应添加该边缘。

对于上述问题,什么是最佳解决方案?

4 个答案:

答案 0 :(得分:1)

在定向图中,假设您要从u添加边到v:

您必须检查v和u是否已连接。您可以使用BFS或DFS来执行此操作。

如果您确定从v到u有连接,那么从u到v添加边将导致循环。

对于Demo,我从互联网上实现了图形,并添加了路径是否存在于v到u的逻辑。此逻辑在以下代码中处于函数doesPathExistBetween(V from, V to)中。

package java8new;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

public class Digraph<V> {

    public static class Edge<V> {
        private V vertex;
        private int cost;

        public Edge(V v, int c) {
            vertex = v;
            cost = c;
        }

        public V getVertex() {
            return vertex;
        }

        public int getCost() {
            return cost;
        }

        @Override
        public String toString() {
            return "Edge [vertex=" + vertex + ", cost=" + cost + "]";
        }

    }

    /**
     * A Map is used to map each vertex to its list of adjacent vertices.
     */

    private Map<V, List<Edge<V>>> neighbors = new HashMap<V, List<Edge<V>>>();

    /**
     * String representation of graph.
     */
    public String toString() {
        StringBuffer s = new StringBuffer();
        for (V v : neighbors.keySet())
            s.append("\n    " + v + " -> " + neighbors.get(v));
        return s.toString();
    }

    /**
     * Add a vertex to the graph. Nothing happens if vertex is already in graph.
     */
    public void add(V vertex) {
        if (neighbors.containsKey(vertex))
            return;
        neighbors.put(vertex, new ArrayList<Edge<V>>());
    }

    /**
     * Add an edge to the graph; if either vertex does not exist, it's added.
     * This implementation allows the creation of multi-edges and self-loops.
     */
    public void add(V from, V to, int cost) {
        this.add(from);
        this.add(to);
        neighbors.get(from).add(new Edge<V>(to, cost));
    }

    public List<V> outboundNeighbors(V vertex) {
        List<V> list = new ArrayList<V>();
        for (Edge<V> e : neighbors.get(vertex))
            list.add(e.vertex);
        return list;
    }

    public void bfs(V root) {
        // Since queue is a interface
        Queue<V> queue = new LinkedList<V>();
        Map<V, Boolean> visited = new HashMap<V, Boolean>();

        if (root == null)
            return;

        visited.put(root, Boolean.TRUE);
        // Adds to end of queue
        queue.add(root);

        while (!queue.isEmpty()) {
            // removes from front of queue
            V r = queue.remove();
            System.out.println(r.toString());

            // Visit child first before grandchild
            for (V n : outboundNeighbors(r)) {
                if (!visited.containsKey(n)) {

                    queue.add(n);
                    visited.put(n, Boolean.TRUE);
                }
            }
        }
    }

    public Boolean doesPathExistBetween(V from, V to) {
        Queue<V> queue = new LinkedList<V>();
        Map<V, Boolean> visited = new HashMap<V, Boolean>();

        visited.put(from, Boolean.TRUE);

        // Adds to end of queue
        queue.add(from);

        while (!queue.isEmpty()) {
            // removes from front of queue
            V r = queue.remove();

            // Visit child first before grandchild
            for (V n : outboundNeighbors(r)) {
                if (!visited.containsKey(n)) {
                    if (n.equals(to))
                        return Boolean.TRUE;
                    queue.add(n);
                    visited.put(n, Boolean.TRUE);
                }
            }
        }
        return Boolean.FALSE;

    }

    public static void main(String[] args) throws IOException {

        Digraph<Integer> graph = new Digraph<Integer>();
        // Adding vertices
        graph.add(0);
        graph.add(1);
        graph.add(2);
        graph.add(3);
        // Adding Edges with weights
        graph.add(0, 1, 1);
        graph.add(1, 2, 2);
        graph.add(3, 0, 2);

        System.out.println("Path betwen 0 to 2 exists : " + graph.doesPathExistBetween(0, 2));
        System.out.println("Path betwen 1 to 3 exists : " + graph.doesPathExistBetween(1, 3));

        graph.bfs(0);
        graph.bfs(1);

    }
}

答案 1 :(得分:0)

然后直截了当的算法就像:

  1. 遍历图表(BFS将完成工作)
  2. 在探索节点时,请将其标记为“好的,我已经在那里”。您可以使用一些HashSet来执行此操作。
  3. 如果该节点已经在一个集合中,那么您第二次来到它,所以有一个循环 - 您可以安全地返回这个图形有一个循环&#39;答案。
  4. 现在给出状态X(当前状态)的图形不具有循环,它们在顶点A - >之间添加新边缘时。 B可以创建一个新循环。但在这种情况下,A将始终是这种循环的一部分。因此,从A重新开始遍历是有意义的。根据图表的大小和性质,您可以更快地找到它。

答案 2 :(得分:0)

这个问题的算法仍然是研究的主题,但是对于许多实际情况,当你添加边(https://en.wikipedia.org/wiki/Dominator_(graph_theory))时,维护图的支配树有很大帮助。

维护支配树可以按每次添加操作分摊O(log(V + E))时间,或者可能稍好一些。

添加边时仍然需要执行DFS或BFS,但由于循环中的所有顶点都经过相同的直接支配器,因此您只需要搜索将成为同一父节点的子节点的顶点,并且该父节点本身,在支配树中为了检测那个循环。

如果在构建图形时不允许循环,则很可能会有很多支配者,并且搜索每个边缘插入的节点数量会很小。

答案 3 :(得分:0)

如果您正在为 Kruskal 的算法编写代码以查找图的最小生成树,那么您将遇到此用例。 您必须根据权重对所有边进行排序,然后逐一选择以形成图形。对于每条边,你必须检查它是否形成任何循环。

public boolean isThisEdgeFormingLoop(Edge n) {
        int parent=0 ;int rank =1;
        
        int fromParent=parentRankMatrix[parent][n.getFrom()];
        int toParent=parentRankMatrix[parent][n.getTo()];
        if(fromParent==-1 && toParent==-1) {
            if(parentRankMatrix[rank][n.getTo()] > parentRankMatrix[rank][n.getFrom()]) {
                parentRankMatrix[parent][n.getFrom()]=n.getTo();
            }else if(parentRankMatrix[rank][n.getTo()] < parentRankMatrix[rank][n.getFrom()]) {
                parentRankMatrix[parent][n.getTo()]=n.getFrom();
            }else {
                parentRankMatrix[rank][n.getTo()]++;
                parentRankMatrix[parent][n.getFrom()]=n.getTo();
            }
            return false;
        }else {
            int absFromParent=getAbsoluteParent(n.getFrom());
            int absToParent=getAbsoluteParent(n.getTo());   
            if(absFromParent==absToParent) {
                return true;
            }else {
                if(parentRankMatrix[rank][absFromParent] > parentRankMatrix[rank][absToParent]) {
                    parentRankMatrix[parent][absToParent]=absFromParent;
                }else if(parentRankMatrix[rank][absFromParent] < parentRankMatrix[rank][absToParent]){
                    parentRankMatrix[parent][absFromParent]=absToParent;
                }else {
                    parentRankMatrix[rank][absToParent]++;
                    parentRankMatrix[parent][absFromParent]=absToParent;
                }
                return false;
            } 
        }
    }

完整的实现链接和视频说明:

https://github.com/Sandip9021/Ds-Algo/tree/master/src/kruskal

https://www.youtube.com/watch?v=SPIyJOMI5Jc&t=209s