图的分类
无向图与有向图
有向图:图中的边存在方向性
无向图:边无方向性
非加权图与加权图
加权图:图中的每一条边都一个实数预置对应,即每条边都对应于一个权重
非加权图:默认为每条边的权重都是一致的
连通图与非连通图
连通图:图中每个顶点都可达
非连通图:图中存在孤立的点,即没有任何边与之连接
Java中通用描述图的数据结构
下面的代码构建的图是有向图,加权图,非连通图(可能存在孤立的点)
点类 Node
如果存在孤立的点,它的入读和出度都为零
package com.ambitfly.arithmetic.graph;
import java.util.ArrayList;
public 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<>();
}
}
边类 Edge
package com.ambitfly.arithmetic.graph;
public class Edge {
public int weight; //A到B的权重(权值)
public Node from;
public Node to; //从from到to
public Edge(int weight, Node from, Node to) {
this.weight = weight;
this.from = from;
this.to = to;
}
}
图类
package com.ambitfly.arithmetic.graph;
import java.util.HashMap;
import java.util.HashSet;
public class Graph {
public HashMap<Integer,Node> nodes; //点集
public HashSet<Edge> edges; //边集
public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
图的转换接口函数
由于图的表达形式多种多样,如果没有一个统一的数据结构描述图,那么解题的时候就要对每一种图的数据结构实现一套对应的算法。所以要构建一套通用的结构函数,把多种多样的图转化为上面图的数据结构,每次都使用上面的数据结构解题即可。
/**
* 图的转换接口,使用一套代码实现图的所有算法。
* @param matrix
* @return
*/
public static Graph createGraph(Integer[][] matrix) {
Graph graph = new Graph();
for (int i = 0; i < matrix.length; i++) {
Integer from = matrix[i][0]; // from点
Integer to = matrix[i][1]; // to点
Integer weight = matrix[i][2];// 权值
// 如果from点不存在于图中添加
if (!graph.nodes.containsKey(from)) {
graph.nodes.put(from, new Node(from));
}
// 如果to点不存在于图中添加
if (!graph.nodes.containsKey(to)) {
graph.nodes.put(to, new Node(to));
}
//构建from->to这条边
Node nodeFrom = graph.nodes.get(from);
Node nodeTo = graph.nodes.get(to);
Edge edge = new Edge(weight, nodeFrom, nodeTo);
//添加from点的nexts(发散出去的点)添加to点
nodeFrom.nexts.add(nodeTo);
//给from点的出度加1
nodeFrom.out++;
//给to点的入度加1
nodeTo.in++;
//给from点的edges(发散出去的边)添加边
nodeFrom.edges.add(edge);
//给图的边集添加边
graph.edges.add(edge);
}
return graph;
}
图的宽度优先遍历
图的宽度优先遍历(BFS)算法是一个分层搜索的过程,和树的层序遍历算法相同。在图中选中一个节点,作为起始节点,然后按照层次遍历的方式,一层一层地进行访问。
package com.ambitfly.arithmetic.graph;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
/**
* 图的宽度优先遍历
*/
public class Code_BFS {
/**
*
* @param node 从一个点出发
*/
public static void bfs(Node node) {
if (node == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
HashSet<Node> set = new HashSet<>(); //set保证了被访问的节点不会被再次访问
queue.add(node);
set.add(node);
// 类似于树的宽度优先遍历(多了一个Set结构确定图节点是否被访问过)
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.println(cur.value);
for (Node next : cur.nexts) {
if (!set.contains(next)) {
queue.add(next);
set.add(next);
}
}
}
}
}
图的深度优先遍历
深度优先遍历(DFS)就是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。
深度优先遍历图的方法是,从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止
package com.ambitfly.arithmetic.graph;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Stack;
import java.util.logging.Handler;
/**
* 图的深度优先遍历
*/
public class Code_DFS {
public static void dfs(Node node){
if (node == null) {
return;
}
Stack<Node> stack = new Stack<>();
HashSet<Node> set = new HashSet<>();
stack.add(node);
set.add(node);
System.out.println(node.value);
/**
* A -> 打印
* A -> pop
* A -> add
* A.next(first) -> add
* 周而复始
*/
while (!stack.isEmpty()) {
Node cur = stack.pop();
for (Node next : cur.nexts) {
if (!set.contains(next)) {
stack.add(cur);
stack.add(next);
set.add(next);
System.out.println(next.value);
break;
}
}
}
}
}