文章目录
一、概念
图(Graph)是用于表示物体与物体之间存在某种关系的结构。数学抽象后的“物体”称作节点或顶点(Vertex,node或point),节点间的相关关系则称作边。在描绘一张图的时候,通常用一组点或小圆圈表示节点,其间的边则使用直线或曲线。
1、有向图和无向图
图中的边可以是有方向或没有方向的。
例如在一张图中,如果节点表示聚会上的人,而边表示两人曾经握手,则该图就是没有方向的,因为甲和乙握过手也意味着乙一定和甲握过手。相反,如果一条从甲到乙的边表示甲欠乙的钱,则该图就是有方向的,因为“曾经欠钱”这个关系不一定是双向的。前一种图称为无向图,后一种称为有向图。
同时,无向图也可以认为是有向图,即可以想象成两个节点之间有从A到B的边,也有B到A的边,所以从宽泛的角度可以认为所有图都是有向图。
二、图的表示
教材上的表示法:邻接表、邻接矩阵
1、邻接表
2、邻接矩阵
如果图G有n个节点,则邻接矩阵是一个 n*n 的矩阵,定义为:
G[ i ][ j ] = 1(或权重值),若<Vi, Vj>是G中的边;否则G[ i ][ j ] = 无穷大。对于对角线的设置,视情况具体设置。
3、常见的表示法
以上两种表示法在实际刷题的过程中几乎不会遇到。
更多的是给你一个 n*3 的矩阵[ [weight, fromNode, toNode] ],例如[ [3, A, B], [2, B, M], [5, A, R] ],第一个值表示权重,第二个值表示from节点,第三个值表示to节点。也就是一条边一条边的直接表示。
三、图的解决思路
图的算法都不算难,只不过coding的代价比较高
(1)先用自己最熟练的方式,实现图结构的表达
(2)在自己熟悉的结构上,实现所有常用的图算法作为模板
(3)把面试题提供的图结构转化为自己熟悉的图结构,再调用模板或改写即可
图的表示方法这么多种,并且每次给你的形式还可能不同,所以就有必要抽象一个自己的表示方法,以后对于不同的形式,写一个能转换为自己定义形式的方法即可(有种适配器的感觉),这样才能以不变应万变,把不熟悉的表示方法转换为自己熟悉的方法
/**
* 自定义图的信息
*
* @author Java和算法学习:周一
*/
public class Graph {
/**
* 点集,Key:用户给的点,Value:自定义点信息
*/
public HashMap<Integer, Node> nodes;
/**
* 边集
*/
public HashSet<Edge> edges;
public Graph() {
this.nodes = new HashMap<>();
this.edges = new HashSet<>();
}
/**
* 将用户输入的表示边的 N*3 的矩阵转换为自定义的图
*
* @param matrix N*3 的矩阵,[3, 0, 5], [2, 2, 5]
*/
public static Graph createGraph(int[][] matrix) {
Graph graph = new Graph();
for (int[] m : matrix) {
// 拿到用户给的边的权重信息、边的from、to节点
int weight = m[0];
int from = m[1];
int to = m[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);
Edge edge = new Edge(weight, fromNode, toNode);
// 节点信息处理
// 添加 从当前节点出发直接连接的节点、从当前节点出发直接连接的边
fromNode.nexts.add(toNode);
fromNode.edges.add(edge);
// 入度、出度修改
fromNode.out++;
toNode.in++;
// 添加图的边集信息
graph.edges.add(edge);
}
return graph;
}
}
四、图的宽度优先和深度优先遍历
1、宽度优先遍历
(1)准备一个队列,一个Set(存放遍历过的节点,登记表),出发节点为A,把A放到队列和Set中
(2)弹出队列的顶点M,打印M的值。获取M的所有邻居节点next,查看Set中有没有这些next节点,无则放到Set和队列中,有则跳过此next节点
(3)一直执行第2步,直到队列为空