Как выполнить различные базовые обходы графиков? - PullRequest
1 голос
/ 02 апреля 2020

Я пытаюсь выполнить итеративный обход в ширину, итерационный проход в глубину и рекурсивный проход в глубину для данного графа (используя матрицу смежности).

В текущем состоянии моя программа выводит различные неправильные ответы.

Вот несколько примеров.

Я ожидаю

From Node A
DFS (iterative): A B H C D E I F G
DFS (recursive): A B H C D E I F G
BFS (iterative): A B D I H C E F G

, но вместо этого получаю

From Node A
DFS (iterative): A I D B H C F G E 
DFS (recursive): A B H C F D E I G 
BFS (iterative): A B D I H C E F G  

Я не уверен, что проблема с моей программой заключается в реализации обходов или в моей реализации какой-либо другой части программы. Чтобы быть более точным c, я не уверен, что моя реализация connectNode или getNeighbors является причиной неправильного вывода, или это моя реализация обходов.

EDIT: Соседи должны быть выбранным в порядке возрастания, если это важно. Возможно, это является частью проблемы?

EDIT2: я добавил новую строку кода, благодаря предложению @ HylianPikachu. Теперь я получаю полные ответы, но они все еще не в правильном порядке.

EDIT3: я добавил код, чтобы сделать так, чтобы узел root проверялся как посещенный на наличие bfs и рекурсивных dfs. Я думаю. Я должен также отметить, что мне дали части этого кода и сказали заполнить остальное. Мне сказали, что нужно использовать стек и очередь, хотя могут быть и более лучшие варианты.

EDIT4: добавлено то, что было предложено, и теперь Iterative BFS работает и получает правильный результат. Однако оба поиска DSF по-прежнему не работают. Я изменил результаты программы выше, чтобы показать это.

import java.util.*;

public class GraphM {
    public Node rootNode;
    public List<Node> nodes = new ArrayList<Node>(); // nodes in graph
    public int[][] adjMatrix; // adjacency Matrix

    public void setRootNode(Node n) {
        rootNode = n;
    }

    public Node getRootNode() {
        return rootNode;
    }

    public void addNode(Node n) {
        nodes.add(n);
    }

    // This method connects two nodes
    public void connectNode(Node src, Node dst) {

        if(adjMatrix == null) {
            adjMatrix = new int[nodes.size()][nodes.size()];
        }

        adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
        adjMatrix[nodes.indexOf(dst)][nodes.indexOf(src)] = 1;

    }

    // Helper method to get one unvisited node from a given node n.
    private Node getUnvisitedChildNode(Node n) {
        int index = nodes.indexOf(n);
        int size = adjMatrix.length;
        for (int j = 0; j < size; j++)
            if (adjMatrix[index][j] == 1 && ((Node) nodes.get(j)).visited == false)
                return nodes.get(j);
        return null;
    }

    // get all neighboring nodes of node n.
    public List<Node> getNeighbors(Node n) {
        List<Node> neighbors = new ArrayList<Node>();

        for(int i = 0; i < nodes.size(); i ++) {
            if (adjMatrix[nodes.indexOf(n)][i] == 1) {
                neighbors.add(nodes.get(i));
            }
           Collections.sort(neighbors);
        }
        return neighbors;

    }

    // Helper methods for clearing visited property of node
    private void reset() {
        for (Node n : nodes)
            n.visited = false;
    }

    // Helper methods for printing the node label
    private void printNode(Node n) {
        System.out.print(n.label + " ");
    }

    // BFS traversal (iterative version)
    public void bfs() {


        Queue<Node> queue = new LinkedList<Node>();

        queue.add(rootNode);

        while(!queue.isEmpty()) {

            Node node = queue.poll();
            printNode(node);
            node.visited = true;

            List<Node> neighbors = getNeighbors(node); 

            for ( int i = 0; i < neighbors.size(); i ++) {
                Node n = neighbors.get(i);

                if (n != null && n.visited != true) {
                    queue.add(n);
                    n.visited = true;
                }
            }

        }

    }

    // DFS traversal (iterative version)
    public void dfs() {

        Stack<Node> stack = new Stack<Node>();
        stack.add(rootNode);
        while(!stack.isEmpty()){
            Node node = stack.pop();

            if(node.visited != true) {
                printNode(node);
                node.visited = true;
            }

            List<Node> neighbors = getNeighbors(node);
            for (int i = 0; i < neighbors.size(); i++) {
                Node n = neighbors.get(i);
                if(n != null && n.visited != true) {
                    stack.add(n);
                }
            }

        }

    }

    // DFS traversal (recursive version)
    public void dfs(Node n) {

        printNode(n);
        n.visited = true;
        List<Node> neighbors = getNeighbors(n);
        for (int i = 0; i < neighbors.size(); i ++) {
            Node node = neighbors.get(i);
            if(node != null && node.visited != true) {
                dfs(node);
            }
        }

    }

    // A simple Node class
    static class Node implements Comparable<Node> {
        public char label;
        public boolean visited = false;

        public Node(char label) {
            this.label = label;
        }

        public int compareTo(Node node) {
            return Character.compare(this.label,  node.label);
        }
    }


    // Test everything
    public static void main(String[] args) {

        Node n0 = new Node('A');
        Node n1 = new Node('B');
        Node n2 = new Node('C');
        Node n3 = new Node('D');
        Node n4 = new Node('E');
        Node n5 = new Node('F');
        Node n6 = new Node('G');
        Node n7 = new Node('H');
        Node n8 = new Node('I');

        // Create the graph (by adding nodes and edges between nodes)
        GraphM g = new GraphM();
        g.addNode(n0);
        g.addNode(n1);
        g.addNode(n2);
        g.addNode(n3);
        g.addNode(n4);
        g.addNode(n5);
        g.addNode(n6);
        g.addNode(n7);
        g.addNode(n8);

        g.connectNode(n0, n1);
        g.connectNode(n0, n3);
        g.connectNode(n0, n8);
        g.connectNode(n1, n7);
        g.connectNode(n2, n7);
        g.connectNode(n2, n3);
        g.connectNode(n3, n4);
        g.connectNode(n4, n8);
        g.connectNode(n5, n6);
        g.connectNode(n5, n2);

        // Perform the DFS and BFS traversal of the graph
        for (Node n : g.nodes) {
            g.setRootNode(n);

            System.out.print("From node ");
            g.printNode(n);

            System.out.print("\nDFS (iterative): ");
            g.dfs();
            g.reset();

            System.out.print("\nDFS (recursive): ");
            g.dfs(g.getRootNode());
            g.reset();

            System.out.print("\nBFS (iterative): ");
            g.bfs();
            g.reset();
            System.out.println("\n");
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 02 апреля 2020

Итак, мы уже рассмотрели первую часть вашего вопроса, но я повторю это здесь для тех, кто следует. Всякий раз, когда вы работаете с графами и матрицей смежности, вероятно, лучший способ инициализировать элементы в массиве - это «оба пути».

Вместо простого использования следующего, что потребует, чтобы указанная вершина c была указана первой для того, чтобы найти соседей:

adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;

Используйте это, что приводит к поискам, которые агностичны c порядка вершин:

adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
adjMatrix[nodes.indexOf(dst)][nodes.indexOf(src)] = 1;

Теперь для упорядочения. Вы хотите, чтобы вершины выводились в порядке от «наименьшей» буквы к «наибольшей» букве. Мы рассмотрим каждую из ваших структур данных индивидуально.

В BFS (итеративно) вы используете Очередь. Очереди "первым пришел, первым вышел". Другими словами, элемент, который был добавлен в очередь наименьшим образом, будет выводиться первым при каждом вызове queue.poll(). Таким образом, вам нужно добавить ваши узлы от наименьшего к наибольшему.

В DFS (итеративном) вы используете стек. Стеки являются «последним, первым - первым». Другими словами, элемент, который был недавно добавлен в стек, будет выводиться первым при каждом вызове stack.pop(). Таким образом, вам нужно добавить ваши узлы от наибольшего к наименьшему.

В DFS (рекурсивном) вы используете List. Списки не имеют упорядочения «in-out» как такового, так как мы можем опрашивать их в любом порядке, который мы хотим, но проще всего было бы просто отсортировать Список от наименьшего к наибольшему и вывести их по порядку.

Имея это в виду, нам нужно ввести протокол для сортировки графа. Все три протокола используют getNeighbors(), поэтому мы отсортируем выведенный список сразу после вызова этой функции. Списки можно упорядочить с помощью функции Collections.sort(List l) из java.utils.Collections, но сначала нам нужно изменить класс узлов, чтобы Java знал, как сортировать узлы. Для дальнейшего изучения деталей того, что я делаю, вы можете посмотреть здесь , но этот пост становится намного длиннее, чем я предполагал, поэтому я просто покажу код здесь и позвольте заинтересованные исследуют сами ссылки.

Сначала необходимо настроить класс Node, реализовав Comparable<Node> и добавив функцию compareTo().

static class Node implements Comparable<Node>{
    public char label;
    public boolean visited = false;

    public Node(char label) {
        this.label = label;
    }

    @Override
    public int compareTo(Node that) {
       return Character.compare(this.label, that.label);
    }
}

Затем в тех случаях, когда мы хотим чтобы упорядочить список от наименьшего к наибольшему, мы можем использовать Collections.sort(neighbors). Когда мы хотим от наименьшего к меньшему, мы можем использовать Collections.sort(neighbors, Collections.reverseOrder()). Наш окончательный код будет выглядеть так:

// BFS traversal (iterative version)
public void bfs() {


    Queue<Node> queue = new LinkedList<Node>();

    queue.add(rootNode);

    while(!queue.isEmpty()) {

        Node node = queue.poll();
        printNode(node);
        node.visited = true;

        List<Node> neighbors = getNeighbors(node); 
        //NEW CODE: Sort our neighbors List!
        Collections.sort(neighbors);

        for ( int i = 0; i < neighbors.size(); i ++) {
            Node n = neighbors.get(i);

            if (n != null && n.visited != true) {
                queue.add(n);
                n.visited = true;
            }
        }

    }

}

// DFS traversal (iterative version)
public void dfs() {

    Stack<Node> stack = new Stack<Node>();
    stack.add(rootNode);
    while(!stack.isEmpty()){
        Node node = stack.pop();

        if(node.visited != true) {
            printNode(node);
            node.visited = true;
        }

        List<Node> neighbors = getNeighbors(node);
        //NEW CODE: Sort our neighbors List in reverse order!
        Collections.sort(neighbors, Collections.reverseOrder());

        for (int i = 0; i < neighbors.size(); i++) {
            Node n = neighbors.get(i);
            if(n != null && n.visited != true) {
                stack.add(n);
            }
        }

    }

}

// DFS traversal (recursive version)
public void dfs(Node n) {

    printNode(n);
    n.visited = true;
    List<Node> neighbors = getNeighbors(n);
    //NEW CODE: Sort our neighbors List!
    Collections.sort(neighbors);

    for (int i = 0; i < neighbors.size(); i ++) {
        Node node = neighbors.get(i);
        if(node != null && node.visited != true) {
            dfs(node);
        }
    }

}
0 голосов
/ 02 апреля 2020

Я бы предложил разделить вашу проблему на более мелкие части.

Если вы хотите написать класс для неориентированного графа, сначала сделайте это и немного протестируйте.

Если вы хотите посмотреть, можете ли вы реализовать обход, убедитесь, что ваш график работает первым. Вы также можете использовать guava , что позволяет использовать MutableGraph (и многое другое). Здесь - как установить его в случае, если вы используете IntelliJ, а здесь - как использовать графики из гуавы.

Также не забудьте использовать отладчик, чтобы узнать если ваш код не работает.

...