如何对图进行不同的基本遍历?
How to perform different basic traversals of graphs?
我正在尝试对给定图执行迭代广度优先遍历、迭代深度优先遍历和递归深度优先遍历(使用邻接矩阵)。
在当前状态下,我的程序输出各种错误答案。
这里有一些例子。
我很期待
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
我不确定我的程序的问题是在于遍历的实现,还是在于我对程序其他部分的实现。更具体地说,我不确定是我的实现 connectNode 或 getNeighbors 方法导致了不正确的输出,还是我的遍历实现。
编辑:如果这很重要,应该按升序选择邻居。也许这是问题的一部分?
EDIT2:感谢@HylianPikachu 的建议,我添加了新的代码行。我现在得到了完整的答案,但它们的顺序仍然不正确。
EDIT3:我添加了代码以使其成为 bfs 和递归 dfs 的访问根节点。我认为。我还应该注意,我得到了这段代码的一部分,并被告知要填写其余部分。堆栈和队列的使用是我被告知要使用的,尽管可能有更好的选择。
EDIT4:添加了所建议的内容,现在,迭代 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");
}
}
}
我建议将您的问题分解成更小的部分。
如果你想为无向图编写 class,请先编写并测试一下。
如果你想看看你是否可以实现遍历,首先要确保你的图能正常工作。您还可以使用 guava, which lets you use MutableGraph (and lots more). Here is how to install it in case you're using IntelliJ and here 是如何使用来自番石榴的图表。
另外记得使用调试器来找出您的代码是否出错。
所以,我们已经涵盖了你问题的第一部分,但我会在这里为后续的人重申一下。每当使用图形和邻接矩阵时,初始化数组中元素的最佳方法可能是 "both ways."
与其仅使用以下内容,还需要首先列出特定顶点才能找到邻居:
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
使用这个,它会导致与顶点顺序无关的搜索:
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
adjMatrix[nodes.indexOf(dst)][nodes.indexOf(src)] = 1;
现在,订购。您希望按照从 "least" 字母到 "greatest" 字母的顺序输出顶点。我们将单独处理您的每个数据结构。
在 BFS(迭代)中,您使用队列。队列是"first in, first out." 换句话说,每当您调用queue.poll()
时,最近最少添加到队列中的元素将首先输出。因此,您需要从最小到最大添加节点。
在 DFS(迭代)中,您使用堆栈。 Stacks是"last in, first out." 换句话说,当你调用stack.pop()
时,最近添加到Stack中的元素将首先输出。因此,您需要从大到小添加节点。
在 DFS(递归)中,您使用列表。列表本身没有 "in-out" 排序,因为我们可以按我们想要的任何顺序轮询它们,但最简单的事情就是将列表从最小到最大排序并按顺序输出它们。
考虑到这一点,我们需要引入对图进行排序的协议。这三个协议都使用 getNeighbors()
,因此我们将在调用该函数后立即对输出的列表进行排序。可以使用 java.utils.Collections
中的函数 Collections.sort(List l)
对列表进行排序,但我们首先需要修改您的节点 class,以便 Java 知道如何对节点进行排序。要进一步阅读我正在做的事情的详细信息,您可以查看 here,但是这个 post 比我预期的要长得多,所以我将在这里展示代码,让感兴趣的人自己去探索link。
您首先要通过实施 Comparable<Node>
并添加 compareTo()
函数来调整您的节点 class。
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);
}
}
然后,如果我们想对List进行从小到大的排序,我们可以使用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);
}
}
}
我正在尝试对给定图执行迭代广度优先遍历、迭代深度优先遍历和递归深度优先遍历(使用邻接矩阵)。
在当前状态下,我的程序输出各种错误答案。
这里有一些例子。
我很期待
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
我不确定我的程序的问题是在于遍历的实现,还是在于我对程序其他部分的实现。更具体地说,我不确定是我的实现 connectNode 或 getNeighbors 方法导致了不正确的输出,还是我的遍历实现。
编辑:如果这很重要,应该按升序选择邻居。也许这是问题的一部分?
EDIT2:感谢@HylianPikachu 的建议,我添加了新的代码行。我现在得到了完整的答案,但它们的顺序仍然不正确。
EDIT3:我添加了代码以使其成为 bfs 和递归 dfs 的访问根节点。我认为。我还应该注意,我得到了这段代码的一部分,并被告知要填写其余部分。堆栈和队列的使用是我被告知要使用的,尽管可能有更好的选择。
EDIT4:添加了所建议的内容,现在,迭代 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");
}
}
}
我建议将您的问题分解成更小的部分。
如果你想为无向图编写 class,请先编写并测试一下。
如果你想看看你是否可以实现遍历,首先要确保你的图能正常工作。您还可以使用 guava, which lets you use MutableGraph (and lots more). Here is how to install it in case you're using IntelliJ and here 是如何使用来自番石榴的图表。
另外记得使用调试器来找出您的代码是否出错。
所以,我们已经涵盖了你问题的第一部分,但我会在这里为后续的人重申一下。每当使用图形和邻接矩阵时,初始化数组中元素的最佳方法可能是 "both ways."
与其仅使用以下内容,还需要首先列出特定顶点才能找到邻居:
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
使用这个,它会导致与顶点顺序无关的搜索:
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
adjMatrix[nodes.indexOf(dst)][nodes.indexOf(src)] = 1;
现在,订购。您希望按照从 "least" 字母到 "greatest" 字母的顺序输出顶点。我们将单独处理您的每个数据结构。
在 BFS(迭代)中,您使用队列。队列是"first in, first out." 换句话说,每当您调用queue.poll()
时,最近最少添加到队列中的元素将首先输出。因此,您需要从最小到最大添加节点。
在 DFS(迭代)中,您使用堆栈。 Stacks是"last in, first out." 换句话说,当你调用stack.pop()
时,最近添加到Stack中的元素将首先输出。因此,您需要从大到小添加节点。
在 DFS(递归)中,您使用列表。列表本身没有 "in-out" 排序,因为我们可以按我们想要的任何顺序轮询它们,但最简单的事情就是将列表从最小到最大排序并按顺序输出它们。
考虑到这一点,我们需要引入对图进行排序的协议。这三个协议都使用 getNeighbors()
,因此我们将在调用该函数后立即对输出的列表进行排序。可以使用 java.utils.Collections
中的函数 Collections.sort(List l)
对列表进行排序,但我们首先需要修改您的节点 class,以便 Java 知道如何对节点进行排序。要进一步阅读我正在做的事情的详细信息,您可以查看 here,但是这个 post 比我预期的要长得多,所以我将在这里展示代码,让感兴趣的人自己去探索link。
您首先要通过实施 Comparable<Node>
并添加 compareTo()
函数来调整您的节点 class。
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);
}
}
然后,如果我们想对List进行从小到大的排序,我们可以使用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);
}
}
}