一 图的概念
1)由点的集合和边的集合构成
2)虽然存在有向图和无向图的概念,但实际上都可以用有向图来表达
3)边上可能带有权值
二 图结构的表达
1)邻接表法
2)邻接矩阵法
3)除此之外还有其他众多的方式 一个边一个边的人给你
2.1 邻接表法
2.2 邻接矩阵法
面试中经常遇到的 一个一个边给你
【3,0 ,7 】 0到7 权重3
三、 图的面试题如何搞定
图的算法都不算难,只不过coding的代价比较高
1)先用自己最熟练的方式,实现图结构的表达
2)在自己熟悉的结构上,实现所有常用的图算法作为模板
3)把面试题提供的图结构转化为自己熟悉的图结构,再调用模板或改写即可
四 、图的转化器
图的转换器 GraphGenerator
4.1 代码
import java.util.HashMap;
import java.util.HashSet;
//图由点集和边集组成
public class Graph {
//点集 整数就是当前的数值,node对应数值生成的node
public HashMap<Integer, Node> nodes;
//边集
public HashSet<Edge> edges;
public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
package class16;
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;
}
}
package class16;
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<>();
}
}
public class GraphGenerator {
// matrix 所有的边
// N*3 的矩阵
// [weight, from节点上面的值,to节点上面的值]
//
// [ 5 , 0 , 7]
// [ 3 , 0, 1]
//
//三条边
[
[ 5 , 0 , 7],
[ 5 , 0 , 7],
[ 5 , 0 , 7]
]
public static Graph createGraph(int[][] matrix) {
Graph graph = new Graph();
for (int i = 0; i < matrix.length; i++) {
// 拿到每一条边, matrix[i]
int weight = matrix[i][0];
int from = matrix[i][1];
int to = 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);
Edge newEdge = new Edge(weight, fromNode, toNode);
fromNode.nexts.add(toNode);
fromNode.out++;
toNode.in++;
fromNode.edges.add(newEdge);
graph.edges.add(newEdge);
}
return graph;
}
}
五、 图的宽度优先&深度优先遍历
5.1 图的宽度优先分析
描述 -宽度优先遍历
1,利用队列实现
2,从源节点开始依次按照宽度进队列,然后弹出
3,每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
4,直到队列变空
为哈要加set 因为图是有回路的,如果没有set判断某个节点是否打印过,就会出现循环打印的情况,没完没了
5.2 代码
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
public class Code01_BFS {
// 从node出发,进行宽度优先遍历
public static void bfs(Node start) {
if (start == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
HashSet<Node> set = new HashSet<>();
queue.add(start);
set.add(start);
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.println(cur.value);
for (Node next : cur.nexts) {
if (!set.contains(next)) {
set.add(next);
queue.add(next);
}
}
}
}
}
import java.util.*;
class Graph {
private int V; // 图中节点数量
private LinkedList<Integer> adj[]; // 存储每个节点相连接的其他节点列表
public Graph(int v) {
V = v;
adj = new LinkedList[V];
for (int i=0; i < V; ++i)
adj[i] = new LinkedList();
}
public void addEdge(int v, int w) {
adj[v].add(w);
}
public void BFS(int s) {
boolean visited[] = new boolean[V];
Queue<Integer> queue = new LinkedList<>();
visited[s]=true;
System.out.print("BFS Traversal starting from " + s + ": ");
queue.add(s);
while (!queue.isEmpty()) {
s = queue.poll();
Iterator<Integer> i = adj[s].listIterator();
while (i.hasNext()) {
int n = i.next();
if (!visited[n]) {
visited[n] = true;
System.out.print(n + " ");
queue.add(n);
}
}
}
}
}
public class Main {
public static void main(String args[]) {
Graph g = new Graph(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(2, 3);
g.BFS(0);
}
}
5.3深度优先遍历 分析
set 打印过的节点放到set里面
描述-深度优先遍历 一条路没走完就一直走
1,利用栈实现
2,从源节点开始把节点按照深度放入栈,然后弹出
3,每弹出一个点,把该节点下一个没有进过栈的邻接点放入栈
4,直到栈变空
5.4 代码
package class16;
import java.util.HashSet;
import java.util.Stack;
public class Code02_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);
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;
}
}
}
}
}
import java.util.*;
class Graph {
private int V; // 图中节点数量
private LinkedList<Integer> adj[]; // 存储每个节点相连接的其他节点列表
public Graph(int v) {
V = v;
adj = new LinkedList[V];
for (int i=0; i<v; ++i)
adj[i] = new LinkedList();
}
public void addEdge(int v, int w) {
adj[v].add(w);
}
public void DFSUtil(int v, boolean visited[]) {
visited[v] = true;
System.out.print(v + " ");
Iterator<Integer> itr = adj[v].iterator();
while (itr.hasNext()) {
int nbr = itr.next();
if (!visited[nbr])
DFSUtil(nbr, visited);
}
}
public void DFSTraversal() {
boolean[] visited = new boolean[V];
for (int i=0; i<V; ++i)
visited[i] = false;
for (int i=0; i<V; ++i)
if (!visited[i])
DFSUtil(i, visited);
}
}
public class Main {
public static void main(String args[]) {
Graph g = new Graph(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(2, 3);
System.out.println("DFS Traversal starting from vertex 0");
g.DFSTraversal();
}
}