目录 | java刷题 | 图_经典结构+图宽度优先遍历+图广/深度优先遍历
- 背景
- 实现
- 分析
- 步骤
- 1、图_经典结构:图、节点、边
- 2、图宽度优先遍历:队列
- 3、图广度优先遍历:栈
- 题解
- 1、图_经典结构
- 2、图宽度优先遍历
- 3、图广度优先遍历
- 4、对于问题编写接口
- 总结
背景
刷题+总结+进步!
看B站左神的算法,总结一个自己的关于图的万能结构,之后遇到新的题目,只用写接口即可与自己的万能结构结合起来,加快做题速度
实现
分析
图_经典结构+图宽度优先遍历+图广度优先遍历。
步骤
1、图_经典结构:图、节点、边
1、定义图;
2、定义图节点;
3、定义图中两点连线的边;
2、图宽度优先遍历:队列
1、利用队列实现;
2、从源节点开始依次按照宽度入队,后弹出
3、每弹出一个节点,将该节点的邻接点中未进入队列的点放入队列;
4、直到队列变空。
3、图广度优先遍历:栈
1、利用栈实现;
2、从源节点开始,将节点按照深度放入栈,后弹出;
3、每弹出一个点,将该点的邻接点中 未进入过栈的点 入栈;
4、直到栈变空。
题解
注意代码中类和方法的位置:
- 以下代码中,需要定义三个类:Graph、Node、Edge、Solution。
- 前三个类不加public,是简单的结构定义。
- Solution类中实现主要的功能函数:也可以加入主函数main实现输入输出,在该类的其他方法中直接调用dfs、bfs功能函数或在其他类中通过类名来调用bfs、dfs。
1、图_经典结构
//图结构
class Graph(){
public HashMap<Integer,Node> nodes; //城市的编号、对应的具体节点,构成map点集。可以通过点集查某个城市是否出现过
//如果数据比较大的话,自己可用数组结构替代hash表,城市的数值不会很大,数组的查改速度会更快一点。
public HashSet<Edge> edges; //边集是一个hashset保存
public Graph(int ){
nodes = new HashMap<>();
edges = new HashMap<>();
}
}
//点结构
class Node{
public int value; //自己的数据项,0 这个城市就是0
public int in; //一个点的入度
public int out; //该点的出度
public ArrayList<Node> nexts; //从当前点出发,发散出去的边直接相连的连接点。
public ArrayList<Node> edges; //当前点出发,出度的边集
public Node(int value){
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
//边结构
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;
}
}
2、图宽度优先遍历
public class Solution{
//从node出发,图宽度优先遍历:队列
public void bfs(Node node){
if(node == null) return null;
//1.利用队列实现
Queue<Node> queue = new LinkedList<>(); //用来进行遍历的临时保存
HashSet<Node> set = new HashSet<>(); //为队列服务,set保证队列中的点集不重复
queue.add(node); //将出发点放到queue、set中
set.add(node);
//4.直到队列变空
while(!queue.isEmpty()){
//2.从源节点开始依次按照宽度进入队列,然后弹出
Node cur = queue.poll();
System.out.println(cur.value); //出节点的时候,进行自己的处理
//3、每弹出一个点,把该节点没有进过队列的邻接点放入队列
for(Node next : cur.nexts){
if(!set.contains(next)){
set.add(next);
queue.add(next);
}
}
}
}
}
3、图广度优先遍历
public class Solution{
//图深度优先遍历:栈
public void dfs(Node node){
if(node == null){
return;
}
Stack<Node> stack = new Stack<>(); //临时存放遍历到的节点,最深路径
HahsSet<Node> set = new HashSet<>(); //存放所有遍历过的节点
stack.add(node);
set.add(node);
while(!stack.isEmpty()){
Node cur = stack.pop();
for(Node next : cur.nexts){ //遍历当前节点的出度对应的节点集合
if(!set.contains(next)){
set.add(cur);
stack.push(cur);
stack.push(next);
System.out.println(next.value);
break; //跳出当前for,遍历cur的下一个出度的节点
}
}
} //直到遍历完所有的节点,依次出栈即可
}
}
4、对于问题编写接口
//接口函数,用户给出一些值,需要将这些值转换为图结构
// matrix 所有的边
// N*3矩阵
// [from节点上的值, to节点上的值, weight]public class Solution{
public class Solution(){
//创建图,接口函数。不要哪些,不填即可。
public Graph createGraph(int[][] matrix){
Graph graph = new Graph();
//将输入的矩阵转换为自己构建的图结构
for(int i = 0; i < matrix.length; i++){ //matrix[0][0],matrix[0][1],matrix[0][2]
Integer from = matrix[i][0];
Integer to = matrix[i][1];
Integer weight = matrix[i][2];
//保证点集里面一定有from 、 to
if(!graph.nodes.containsKey(from)){ //如果from这个城市没有出现过
graph.nodes.put(from, new Node(from));
}
if(!graph.nodes.containsKey(to)){ //如果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(num);
fromNode.out++;
toNode.in++;
fromNode.edges.add(newEdge);
graph.edges.add(newEdge);
}
return graph;
}
}
总结
搁置了好久的博客开始恢复吧