图的基本结构以及BFS和DFS(递归和非递归)
- 完整的图结构
- 有向图建图以及BFS和DFS
- 无向图建图以及BFS和DFS
完整的图结构
- 图的每个顶点包括顶点的值、入度、出度、和它相邻的点(或者在有向图中就是下一个可以到达的点)的集合、以及以它为起点出发的边的集合;
- 图的每条边包括边的权值、边的起点、边的终点;
- 一个图包括点的集合和边的集合;
综上可以得到如下的图结构:
//点
private static class Node{
public int value;
public int in; //入度
public int out; //出度
public ArrayList<Node>nexts;
public ArrayList<Edge>edges;
public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
//边
private static class Edge{
public int weight;
public Node from;
public Node to;
public Edge( Node from, Node to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
//图
private static class Graph{
HashMap<Integer,Node>nodes;
HashSet<Edge>edges;
public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
有向图建图以及BFS和DFS
这里使用一个二维矩阵来表示图(每一行都代表输入一条边),第一列表示的是边的起点、第二列是边的终点、第三列是边的权值。如下例子:
使用上面的例子来建图并进行BFS(广度遍历)和DFS(深度遍历)的代码如下: (在遍历的过程中边的集合几乎用不到)
import java.util.*;
public class GraphTest {
//点
private static class Node{
public int value;
public int in; //入度
public int out; //出度
public ArrayList<Node>nexts;
public ArrayList<Edge>edges;
public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
//边
private static class Edge{
public int weight;
public Node from;
public Node to;
public Edge( Node from, Node to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
//图
private static class Graph{
HashMap<Integer,Node>nodes;
HashSet<Edge>edges;
public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
public static void main(String[] args) {
//每一列分别代表的是 边的 起点,终点,和权值
Integer[][] martix = {
{1,2,4},
{1,3,5},
{2,4,8},
{2,5,6},
{2,8,2},
{3,7,1},
{5,4,2},
{5,6,7},
{6,4,3},
};
Graph G = createGraph(martix);
System.out.println("----------BFS-----------");
BFS(G.nodes.get(1));
System.out.println();
System.out.println("----------DFS-----------");
dfs(G.nodes.get(1),new HashSet<>());
System.out.println();
System.out.println("----------DFS-----------");
dfsnonrecursion(G.nodes.get(1));
}
//建图
public static Graph createGraph(Integer[][] matrix) {
Graph graph = new Graph();
for(int i = 0; i < matrix.length; i++){
Integer from = matrix[i][0];
Integer to = matrix[i][1];
Integer weight = matrix[i][2];
if(!graph.nodes.containsKey(from)) graph.nodes.put(from,new Node(from));
if(!graph.nodes.containsKey(to)) graph.nodes.put(to,new Node(to));
Node fromNode = graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
fromNode.nexts.add(toNode);//有向图
fromNode.out++;
toNode.in++;
Edge newEdge = new Edge(fromNode, toNode, weight);
fromNode.edges.add(newEdge);
graph.edges.add(newEdge);
}
return graph;
}
//BFS
private static void BFS(Node node){
if(node == null)return;
Queue<Node> que = new LinkedList<>();
HashSet<Node>set = new HashSet<>(); //相当于记录是否访问过
que.add(node);
set.add(node);
while(!que.isEmpty()){
Node cur = que.poll();
System.out.print(cur.value+ " ");
for(Node next : cur.nexts){
if(!set.contains(next)){
set.add(next);
que.add(next);
}
}
}
}
//递归的
private static void dfs(Node node,HashSet<Node>set) {
if(node == null)return;
set.add(node);
System.out.print(node.value + " ");
for(Node next : node.nexts){
if(!set.contains(next)) dfs(next,set);
}
}
//非递归的
private static void dfsnonrecursion(Node node) {
if(node == null)return;
Stack<Node>stack = new Stack<>();
HashSet<Node>set = new HashSet<>();
stack.push(node);
System.out.print(node.value + " ");
set.add(node);
while(!stack.isEmpty()){
Node cur = stack.pop();
for(Node next : cur.nexts){
if(!set.contains(next)){
stack.push(cur); //注意这个也要入栈
stack.push(next);
set.add(next);
System.out.print(next.value + " ");
break;
}
}
}
}
}
- BFS就是按照到起点的距离(按层次)遍历;
- DFS就是只要某个结点可以往下走,就一直走下去(一条路走到黑),走不了了再回溯回来;
- 其中非递归的DFS使用栈完成,注意,访问某个结点的next结点集合的时候,只访问一个,然后结点本身还是要先入栈;
- BFS和DFS都要记录某个结点是否已经被访问过;
上面例子的遍历结果:
无向图建图以及BFS和DFS
无向图和有向图的区别就是度没有出度和入度之分,结点的nexts域、和结点相邻的边集要相互的添加;
看下图的例子:
建图以及BFS和DFS遍历如下: (这里省去了边的集合)
import java.io.BufferedInputStream;
import java.util.*;
//无向图BFS和DFS
public class BFS_DFS {
private static class Node{
public int value;
public ArrayList<Node>nexts;
public Node(int value) {
this.value = value;
nexts = new ArrayList<>();
}
}
private static class Graph{
HashMap<Integer,Node>nodes;
public Graph() {
nodes = new HashMap<>();
}
}
//建一个无向图
public static void main(String[] args) {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
while(cin.hasNext()){
int n = cin.nextInt();
int m = cin.nextInt();
if(n == 0 && m == 0)break;
Graph G = new Graph();
for(int i = 0; i < m; i++){
int a = cin.nextInt();
int b = cin.nextInt();
if(!G.nodes.containsKey(a))G.nodes.put(a,new Node(a));
if(!G.nodes.containsKey(b))G.nodes.put(b,new Node(b));
Node from = G.nodes.get(a);
Node to = G.nodes.get(b);
from.nexts.add(to);
to.nexts.add(from); //无向图
}
System.out.println("---------------BFS----------------");
BFS(G.nodes.get(1)); // 从某个结点开始BFS遍历
System.out.println();
System.out.println("---------------DFS----------------");
dfs(G.nodes.get(1),new HashSet<>());
System.out.println();
System.out.println("---------------DFS----------------");
dfsnonrecursion(G.nodes.get(1));
System.out.println();
}
}
private static void BFS(Node node){
if(node == null)return;
Queue<Node>que = new LinkedList<>();
HashSet<Node>set = new HashSet<>(); //相当于记录是否访问过
que.add(node);
set.add(node);
while(!que.isEmpty()){
Node cur = que.poll();
System.out.print(cur.value+ " ");
for(Node next : cur.nexts){
if(!set.contains(next)){
set.add(next);
que.add(next);
}
}
}
}
//递归的
private static void dfs(Node node,HashSet<Node>set) {
if(node == null)return;
set.add(node);
System.out.print(node.value + " ");
for(Node next : node.nexts){
if(!set.contains(next)) dfs(next,set);
}
}
//非递归的
private static void dfsnonrecursion(Node node) {
if(node == null)return;
Stack<Node>stack = new Stack<>();
HashSet<Node>set = new HashSet<>();
stack.push(node);
System.out.print(node.value + " ");
set.add(node);
while(!stack.isEmpty()){
Node cur = stack.pop();
for(Node next : cur.nexts){
if(!set.contains(next)){
stack.push(cur); //注意这个也要入栈
stack.push(next);
set.add(next);
System.out.print(next.value + " ");
break;
}
}
}
}
}
遍历结果