图的存储方式:邻接表、邻接矩阵
图的题目采用不同的存储方式,coding不同,但是算法是一致的。
//图的结构1
public class graph {
public class Node {
int value;
Node left;
Node right;
}
public HashMap<Integer,Node> nodes;
public HashSet<Edge> edges;
public Graph(){
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
建立图
public class CreateGraph {
public static Graph createGraph(Integer[][] matrix){
Graph graph = new Graph();
for (int i = 0; i < matrix.length; i++) {
int from = matrix[i][0];
int to = matrix[i][1];
int weight = matrix[i][2];
if (!graph.nodes.containsKey(from)){
graph.nodes.put(from,new Node(from));//加入编号为from的新节
}
if(!graph.nodes.containsKey(to)){
graph.nodes.put(to,new Node(to));
}
/*经过上述阶段就确定了有from 和to 的节点*/
Node fromNode =graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
/*得到了from和to的节点后,就可以往cur的节点填充信息*/
Edge newEdge = new Edge(weight,fromNode,toNode);
fromNode.nexts.add(toNode);
fromNode.out++;
toNode.in++;
fromNode.edges.add(newEdge);//给from添加一条出度
graph.edges.add((newEdge));
}
return graph;
}
}
图的宽度优先遍历:
利用队列实现;
从源节点开始依次按照宽度进入队列,然后弹出
每弹出一个点,把该节点所有没有进过对垒的邻接点放入队列
直到队列变空
public class GraphBFS {
public static void graphBFS(Node head){
Queue<Node> queue = new LinkedList<Node>();
HashSet<Node> set = new HashSet<>();
queue.add(head);
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);
}
}
}
}
}
广度优先遍历:
利用栈实现
从源节点开始把点按照节深度入栈,然后弹出
每弹出一个点,把该节点下一个没有进过栈的邻接点放入到栈内
直到栈变空
public class GraphDFS {
public static void graphdfs(Node head){
Stack<Node> stack = new Stack<Node>();
HashSet<Node> set = new HashSet<Node>();
stack.push(head);
set.add(head);
while(!stack.isEmpty()){
Node cur = stack.pop();
System.out.println(head.value);
for(Node next: cur.nexts){
if(!set.contains(next)){
stack.push(cur);
stack.push(next);
set.add(next);
System.out.println(next.value);
break;//跳出这个for循环
}
}
}
}
}
拓扑排序:可以有效解决编译依赖这类型的问题
拓扑排序必须遵守的要求:不能有环
public class SortedTopology {
public static List<Node> sortedtopology(Graph graph){
HashMap<Node,Integer> inMap = new HashMap<>();//用来春芳每个的入度
Queue<Node> zeroInQueue = new LinkedList<>();//用来存放所有入度为零的节点
for(Node node:graph.nodes.values()){
inMap.put(node,node.in);//把所有的点情况都存放到inMap中
if(node.in==0){
zeroInQueue.add(node);
}
}
/*以上为准备工作*/
List<Node> result = new ArrayList<>();//作为输出结果
while (!zeroInQueue.isEmpty()){
Node cur = zeroInQueue.poll();
result.add(cur);
for (Node next:cur.nexts){
inMap.put(next,inMap.get(next)-1);//入度减1
if(inMap.get(next)==0){
zeroInQueue.add(next);
}
}
}
return result;
}
}
kruskal算法:从最小的边开始,只要不形成环就可以加上
判断算法不形成环:用集合思想:相连的边形成一个集合,每次加入一条边后改变的出边若存在于其中改变的一个集合的话就可认为形成环。
public class Kruskal {
public static class Mysets{
public HashMap<Node, List<Node>> setMap = null;
public Mysets(List<Node> nodes) {//初始化,把所有的点都辅导setMap里面
for (Node cur : nodes) {
List<Node> set=new ArrayList<Node>();
set.add(cur);
setMap.put(cur,set);
}
}
public boolean IsSameSet(Node from,Node to){
List<Node> fromset =setMap.get(from);
List<Node> toset = setMap.get(to);
return fromset==toset;//判断边是否在统一集合中
}
public void union(Node from,Node to){
List<Node> fromset =setMap.get(from);
List<Node> toset = setMap.get(to);
for(Node toNode:toset){
fromset.add(toNode);
setMap.put(toNode,fromset);
}
}
}
public static Set<Edge> kruskalMST(Graph graph){
for (int i = 0; i < graph.nodes.size(); i++) {
Mysets mysets = new Mysets(graph.nodes.values());
}
}
}
prim算法:适用范围:无向图
随意找一个点:将与其相连的边解锁,找出最短的边,将与to的节点相连的边解锁,周而复始,直到所有的点都存到集合中。
Dijkstra算法:每次加入一个点,重新找到一条最短路径,锁定该点然后将加入的点重新遍历一遍,找出加入点后可以锁定的最近目的点,周而复始。