为Java Graph类实现间接连接测试

时间:2010-08-23 19:42:09

标签: java methods graph testing edge

我正在用Java编写一个类来表示Graph数据结构。这是一个无向,未加权的图形,它的目的主要是边缘测试(节点A是直接或间接连接到节点B)。

我需要帮助实现indirectEdgeTest方法。在下面的代码中,我只是评论了这个方法而且我返回false,所以代码将按原样编译。

我已经花了一些时间来提出算法,但我似乎找不到比这更简单的东西,我担心我会让它变得比它需要的更复杂:

- test first for a direct connection
- if no direct connection exists from node a to node b:
    - for every edge i connected to node a:
        - create a new graph that does not contain edge a -> i
        - test new graph for indirect connectivity between nodes i and b

您的答案中欢迎使用伪代码或实际Java代码。谢谢你的帮助。这是我的代码:

class Graph {

    // This is for an undirected, unweighted graph
    // This implementation uses an adjacency matrix for speed in edge testing 

    private boolean[][] edge;
    private int numberOfNodes;

    public Graph(int numNodes) {

        // The indices of the matrix will not be zero-based, for clarity,
        // so the size of the array will be increased by 1.

           edge = new boolean[numNodes + 1][numNodes + 1];
           numberOfNodes = numNodes;
    }

    public void addEdge(int a, int b) {
        if (a <= numberOfNodes && a >= 1) {
            if (b <= numberOfNodes && b >= 1) {
                edge[a][b] = true;
                edge[b][a] = true;
            }
        }
    }

    public void removeEdge(int a, int b) {
        if (a <= numberOfNodes && a >= 1) {
            if (b <= numberOfNodes && b >= 1) {
                edge[a][b] = false;
                edge[b][a] = false;
            }
        }
    }

    public boolean directEdgeTest(int a, int b) {

        // if node a and node b are directly connected, return true 

        boolean result = false;
        if (a <= numberOfNodes && a >= 1) {
            if (b <= numberOfNodes && b >= 1) {
                if (edge[a][b] == true) {
                    result = true;
                }
            }
        }
        return result;
    }

    public boolean indirectEdgeTest(int a, int b) {

        // if there exists a path from node a to node b, return true 

            // implement indirectEdgeTest algorithm here.

            return false;
    }
}

3 个答案:

答案 0 :(得分:2)

呃,这种方法听起来非常低效。那个怎么样:

void walk(Node orgin, Set<Node> visited) {
    for (Node n : origin.neighbours) {
        if (!visited.contains(n)) {
            visited.add(n);
            walk(n, visited);
        }
    }
}


boolean hasPath(Node origin, Node target) {
    Set<Node> reachables = new HashSet<Node>();
    walk(origin, reachables);
    return reachables.contains(target);
}

此外,使用邻接矩阵对于图遍历是有问题的,因为您无法在稀疏图中有效地迭代节点的邻居。

如果频繁使用该方法,并且图表很少更改,则可以通过预先分解到connected regions并为每个节点存储它所属的区域来加快查询速度。然后,如果它们属于同一个区域,则连接两个节点。

编辑:澄清如何最好地表示图表。对于直接边缘测试,优选邻接矩阵。对于路径测试,分解为区域。随着图形的变化,后者保持当前并非易事,但在文献中可能存在算法。或者,邻接列表可用于图形遍历并因此可用于路径测试,但它们仍然比直接将分解记录到连接区域中的效率低。您还可以使用邻接集将稀疏图中更有效的邻居迭代与恒定时间边缘测试相结合。

请记住,您还可以冗余地存储信息,为每种查询保留定制的独立数据结构。

答案 1 :(得分:1)

您的解决方案可行,但更好的解决方案是从根“a”节点构建生成树。这样,您最终只能考虑一棵树,而不是只缺少特定边的多个子图。

一旦get the idea,您如何实施它取决于您。假设您可以以合理的方式实现算法,您应该只有一棵树来搜索连接,这会大大加快速度。

答案 2 :(得分:1)

我赞美他的答案,但我把这个想法编写成了工作Java类和单元测试,所以我在这里提供一个单独的答案,以防有人在寻找可重复使用的代码。

谢谢meriton。我同意区分直接边缘测试和路径测试很重要,并且有不同的图形实现更适合特定类型的测试。在路径测试的情况下,似乎邻接列表比邻接矩阵表示更有效。

下面的代码可能效率不高,但现在它解决了我的问题。如果有人有改进建议,请随意。

编译:javac Graph.java

执行:java GraphTest

class Graph {

    private java.util.ArrayList<Node> nodeList;
    private int numberOfNodes;

    public Graph(int size) {
        nodeList = new java.util.ArrayList<Node>(size + 1);
        numberOfNodes = size;

        for (int i = 0; i <= numberOfNodes; i++) {
            nodeList.add(new Node());
        }
    }

    public void addEdge(int a, int b) {
        if (a >= 1 && a <= numberOfNodes) {
            if (b >= 1 && b <= numberOfNodes) {
                nodeList.get(a).addNeighbour(nodeList.get(b));
                nodeList.get(b).addNeighbour(nodeList.get(a));
            }
         }
    }

    public void walk(Node origin, java.util.Set<Node> visited) {
        for (Node n : origin.getNeighbours()) {
            if (!visited.contains(n)) {
                visited.add(n);
                walk(n, visited);
            }
        }
    }

    public boolean hasPath(Node origin, Node target) {
        java.util.Set<Node> reachables = new java.util.HashSet<Node>();
        walk(origin, reachables);
        return reachables.contains(target);
    }

    public boolean hasPath(int a, int b) {

        java.util.Set<Node> reachables = new java.util.HashSet<Node>();
        Node origin = nodeList.get(a);
        Node target = nodeList.get(b);
        walk(origin, reachables);
        return reachables.contains(target);       
    }
}

class Node {

    private java.util.Set<Node> neighbours;

    public Node() {
        neighbours = new java.util.HashSet<Node>();
    }

    public void addNeighbour(Node n) {
        neighbours.add(n);
    }

    public java.util.Set<Node> getNeighbours() {
        return neighbours;
    }
}

class GraphTest {

    private static Graph g;

    public static void main(String[] args) {

        g = new Graph(6);

        g.addEdge(1,5);
        g.addEdge(4,1);
        g.addEdge(4,3);
        g.addEdge(3,6);

        printTest(1, 2);
        printTest(1, 4); 
        printTest(6, 1);   
    }

    public static void printTest(int a, int b) {

        System.out.print("Are nodes " + a + " and " + b + " connected?");
        if (g.hasPath(a, b)) {
            System.out.println(" YES.");
        } else {
            System.out.println(" NO.");
        }
    }
}