提示:系列图的文章
提示:大厂笔试面试都可能不咋考的数据结构:图
由于图的结构比较难,出题的时候,很难把这个图的数据搞通顺,而且搞通顺了题目也需要耗费太多时间,故笔试面试都不会咋考
笔试大厂考的就是你的贪心取巧策略和编码能力,这完全不必用图来考你,其他的有大量的选择
面试大厂考你的是优化算法的能力,但是图没啥可以优化的,只要数据结构统一,它的算法是固定死的,所以不会在面试中考!
万一考,那可能都是大厂和图相关的业务太多,比如美团高德地图啥的,这种考的少。
但不管考不考,我们要有基础,要了解图的数据结构和算法。万一考了呢,准备以备不时之需。
图的数据结构比较难,算法是固定的套路
咱们需要统一一种自己熟悉的图的数据结构,方便套用算法时好写!!
下面是咱们得关于图的重要基础知识和重点应用:
(1)图数据结构,图的统一数据结构和非标准图的转化算法
看看二叉树的BFS:
二叉树的宽度优先遍历BFS:按层的遍历方式,请你用队列实现DFS,或者请你用栈实现BFS
用队列实现,x的左右子挨个按顺序进队列。
//复习:这样做,最开始将head加入队列
//开始循环操作:弹出打印。
// 然后看其左右子是否不为null,是就加入队列,否则不管。
public static void bfsBinaryTreePrint(Node head){
if (head == null) return;
LinkedList<Node> queue = new LinkedList<>();
queue.add(head);
while (!queue.isEmpty()){
Node cur = queue.poll();
System.out.print(cur.value + " ");
//按层加入左右子
if (cur.left != null) queue.add(cur.left);
if (cur.right != null) queue.add(cur.right);
}
//图也是这么干BFS,DFS就是用栈,一条道走到黑
}
跟二叉树的BFS类似,也是对于节点x,让x的直接邻居,挨个进队列(当然,没进去过的,进去过的就算了)
(1)a头节点进队列,加入set(标记已经遍历过了),然后开始弹出打印!cur=a;
(2)看a的所有直接邻居,未被遍历过的节点b c,依次进队列,并加入set
(3)依次弹出队列,cur=b,打印,去(1)循环执行。
(4)直到队列为空
超级超级简单!唯一比二叉树多的就是分支多了,二叉树只有2个子,而图可能有1–N个子
另外,就是用set控制已经遍历过的节点,不可以重复遍历。
手撕代码也很简单,这个代码必须手撕会,因为有不少题目都是BFS做的
//复习图的BFS
public static void bfsReview(Node head){
if (head == null) return;
//队列控制BFS,和二叉树一样
Queue<Node> queue = new LinkedList<>();
//集合控制已经遍历过得节点
Set<Node> set = new HashSet<>();
//(1)a头节点进队列,加入set(标记已经遍历过了),然后开始弹出打印!cur=a;
queue.add(head);
set.add(head);
while (!queue.isEmpty()){
Node cur = queue.poll();//弹出打印
System.out.print(cur.value +" ");
//(2)看a的所有直接邻居,未被遍历过的节点b c,依次进队列,并加入set
for(Node next:cur.nexts){
if (!set.contains(next)){
queue.add(next);
set.add(next);
}
}
//(3)依次弹出队列,cur=b,打印,去(1)循环执行。
//(4)直到队列为空
}
}
代码可以说是过于简单了,反正考到你是一定要会的,将来很多题目就在这上面改编你的解
下面生成一个图,然后统一图的数据结构,然后验证bfs
//基础的数据类型,得自个写
//边--和节点是相互渗透的俩数据结构
public static class Edge{
public int weight;
public Node from;
public Node to;
public Edge(int w, Node a, Node b){
weight = w;
from = a;
to = b;
}
}
//节点
public static class Node{
public int in;
public int out;
public int value;
public ArrayList<Node> nexts;
public ArrayList<Edge> edges;//这里是相互渗透的,既然有邻居,就右边
public Node(int v){
value = v;//只需要给这么一个value即可
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
//图结构玩起来,图右边,节也有边
public static class Graph{
public HashMap<Integer, Node> nodes;//v,Node
public HashSet<Edge> edges;
public Graph(){
nodes = new HashMap<>();//一般节点有value,对应包装袋,都是用哈希表玩的,并查集就是这么玩的
edges = new HashSet<>();
}
public int getNodeNum(){
return nodes.size();
}
public int getEdgeNum(){
return edges.size();
}
}
//将非标准图结构转化为左神标准图结构
public static Graph generatGrap(int[][] matrix){
if (matrix == null || matrix.length == 0) return null;
//matrix==
//[1,1,2]
//[2,2,3]
//[3,3,1],w,f,t
//挨个遍历行
Graph graph = new Graph();
for (int i = 0; i < matrix.length; 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 edge = new Edge(weight, fromNode, toNode);//建边
graph.edges.add(edge);
fromNode.out++;
toNode.in++;
fromNode.nexts.add(toNode);
fromNode.edges.add(edge);//除了入度说终点,其余都是说原点
}
return graph;
}
验证bfs
public static void test(){
int[][] matrix = {
{1,1,2},
{2,2,3},
{3,3,5},
{4,2,5},
{5,1,3},
{5,1,4}
};
Graph graph = generatGrap(matrix);
System.out.println("bfs:");
bfs(graph.nodes.get(1));//第一个节点开始遍历打印
System.out.println("\n复习bfs:");
bfsReview(graph.nodes.get(1));//第一个节点开始遍历打印
}
复习bfs:
1 2 3 4 5
提示:重要经验:
1)图的统一数据结构,丰富图结构表示法,要熟悉,就是一堆节点,一堆边,但是他们的属性很丰富,还有标准的构图过程
2)图的宽度优先遍历方法:BFS非常简单,就是沿着x的直接邻居,加入队列就行,弹出打印,不断加队列,很简单。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。