一、图的表示
图的算法不难,但图的表示方法很多,比如邻接表法、邻接矩阵等等。这就导致对于每一种图的表示方法,都要写出该表示方法对应的一套算法。
同时,这也是面试场合经常出现的情况,对于一种不熟悉的图表示方法,往往很难去进行相应算法的转换。
对于这种情况,大佬给出的建议是熟练掌握一种图的表示方法,并掌握所有用该表示方法编写的算法。面试场合只需要去思考如何建立所给图表示方法到熟悉的图表示方法的映射即可。
-
图类
包含所有的点集和所有的边集public class Graph { /** * Integer表示点的编号,Node表示实际的点 */ public HashMap<Integer, Node> nodes; public HashSet<Edge> edges; public Graph() { nodes = new HashMap<>(); edges = new HashSet<>(); } }
一般情况下,Node的编号就是Node的值,且编号范围固定,比如1000个城市之间的连通情况,此时HashMap可以用数组代替,可以提高查询性能
-
Node类
包含从该点出发所指向的下一个点、以该点为起始点的有向边、该点的入度和出度public class Node { /** * in和out分别表示结点的入度和出度 * nexts表示当前节点指向的下一节点 * edges表示从当前节点出发的有向边 */ public int value; public int in; public int out; public ArrayList<Node> nexts; public ArrayList<Edge> edges; public Node(int data) { value = data; in = 0; out = 0; nexts = new ArrayList<>(); edges = new ArrayList<>(); } }
-
Edge类
包含边的起始点、终止点、权重public class Edge { public int weight; public Node from; public Node to; public Edge(int weight, Node from, Node to) { this.weight = weight; this.from = from; this.to = to; } }
二、图的宽度优先遍历BFS
- 算法思想:
- 利用队列实现
- 从源节点开始依次按照宽度进队列,然后弹出
- 每弹出一个点,把该节点所有没有进过队列的邻接点(利用HashSet判断)放入队列
- 直到队列变空
public class BFS {
/**
* 从node出发,进行宽度优先遍历
*/
public static void bfs(Node node) {
if (node == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
// set用于记录已经遍历过的节点
Set<Node> set = new HashSet<>();
queue.add(node);
set.add(node);
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
- 算法思想:
- 利用栈实现
- 从源节点开始把节点按照深度放入栈,然后弹出
- 每弹出一个点,就对该点的邻接点进行判断,找出第一个没有进过访问过的邻接点(利用HashSet判断),把该点和该邻接点依次放入队列,进行下一个点的判断。
- 直到栈变空
- 整个过程中栈中节点的顺序就是深度优先遍历的顺序
public class DFS {
public static void dfs(Node node) {
if (node == null) {
return;
}
Stack<Node> stack = new Stack<>();
Set<Node> set = new HashSet<>();
stack.push(node);
set.add(node);
System.out.println(node.value);
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.println(next.value);
break;
}
}
}
}
}
古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。