图的入门(一)
图的表示方法: 邻接表,邻接矩阵
类型:有向图,无向图
节点信息:入度,出度,节点值,连接的节点,连接的边
package day7; import java.util.ArrayList; public class Node { public int value; public int in; public int out; public ArrayList<Node> next; //这里是list public ArrayList<Edge> edge; public Node(int value) { this.value = value; this.in = 0; this.out = 0; this.next = new ArrayList<>(); this.edge = new ArrayList<>(); } }
边信息:边权重,from的节点,to的节点
package day7; public class Edge { public int value; public Node from; public Node to; public Edge(int value,Node from ,Node to) { this.value = value; this.from = from; this.to = to; } }
图:节点的集合+边的集合
初始化:
package day7; import java.util.HashMap; import java.util.HashSet; public class Graph { public HashMap<Integer, Node> nodes ; public HashSet<Edge> edges; public Graph() { this.nodes = new HashMap<Integer, Node>(); this.edges = new HashSet<Edge>(); } public void creatGraph(int arr[][]) { for (int i = 0;i <arr.length ; i++) {//arr[i] ={from,to,value} Node from,to; if(!this.nodes.containsKey(arr[i][0])) { from = new Node(arr[i][0]); }else { from = this.nodes.get(arr[i][0]); } from.out++; if(!this.nodes.containsKey(arr[i][1])) { to = new Node(arr[i][1]); }else { to = this.nodes.get(arr[i][1]); }; to.in ++; from.next.add(to); Edge edge = new Edge(arr[i][2], from, to); from.edge.add(edge); to.edge.add(edge); this.nodes.put(arr[i][0], from); this.nodes.put(arr[i][1], to); this.edges.add(edge); } } }
图的宽度优先遍历BDF
- 队列实现
- 从一个节点开始,依次按照宽进队列,然后弹出
- 弹出后,查找没有进过队列的临界点入队列
- 至队列为空
package day7; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; public class Code01_BFS { public static void bfs(Node node) { if(node == null) return ; //这里要用LinkList 而不是 PriorityQueue Queue <Node> que = new LinkedList<Node>(); HashSet<Node> set = new HashSet<Node>(); que.add(node); set.add(node); while(!que.isEmpty()) { Node cur = que.poll(); System.out.print(cur.value+" "); for (Node next : cur.next) { if(!set.contains(next)) { que.add(next); set.add(next); } } } } public static void main(String[] args) { // int arr[][] = { // {1,2,7,}, // {1,3,5,}, // {2,3,2,},}; int arr [][]= {{1,2,1},{1,3,2},{2,4,3},{2,5,2},{4,6,4},}; Graph graph = new Graph(); graph.creatGraph(arr); bfs(graph.nodes.get(1)); } } /* * * * */
图的深度优先遍历BDF
- 栈实现
- 从一个节点开始,依次按照深度入栈,然后弹出
- 当前节点弹出后,对比将该节点 下一个没有进过栈的邻接点,入栈。(当前节点也要入栈)
- 至栈空
package day7; import java.util.HashSet; import java.util.Stack; public class Code02_DFS { public static void dfs (Node node) { //每次都忘了 if (node == null) return ; HashSet<Node> set = new HashSet<Node>(); Stack <Node> stack = new Stack<>(); stack.add(node); set.add(node); while(!stack.isEmpty()) { Node cur = stack.pop(); System.out.print(cur.value+ " "); for (Node next : cur.next) { if (! set.contains(next)) { stack.add(next); set.add(next); cur = next; } } } } public static void main(String[] args) { int arr [][]= { {1,2,1},{1,3,2},{2,4,3},{2,5,2},{4,6,4},}; // int arr [][]= { // {1,2,1},{1,3,2},{1,4,2}, // {2,3,1},{2,4,3},{3,4,4},}; Graph graph = new Graph(); graph.creatGraph(arr); dfs(graph.nodes.get(1)); } }
拓扑排序:
要求:存在入度为0的节点的有向图,并且没有环。(3个条件)
package day7; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Queue; public class Code03_TopologicalSort { public static List<Node> topologicalSort (Graph graph) { //这里应该用Map,用set改变了节点的入度信息了,天机set用 add 。map用 put //HashSet<Node> set = new HashSet<Node>(); HashMap<Node, Integer> inMap = new HashMap<Node, Integer>(); Queue<Node> queue = new LinkedList<Node>(); for (Node node: graph.nodes.values()) { inMap.put(node,node.in); if (node.in == 0) { queue.add(node); } } List <Node> result = new ArrayList<Node>(); while (!queue.isEmpty()) { Node cur = queue.poll(); result.add(cur); System.out.print(cur.value+" "); for (Node next : cur.next) { inMap.put(next,inMap.get(next) -1 ); if(inMap.get(next) == 0) { queue.add(next); } } } return result; } public static void main(String[] args) { int arr [][]= {{1,2,1},{1,3,2},{3,2,3},{2,4,3},{2,5,2},{3,5,8},{4,5,3},{4,6,4},}; Graph graph = new Graph(); graph.creatGraph(arr); topologicalSort(graph); } }
最小生成树:保证连通的情况下,边权值和 最小的 集合 ,!!!!!!!函数返回边的集合!!!!!!!
Kruskal:
选边,从小到大(优先级队列),不形成回路(并查集),直到所有点都包括 || 所有边都遍历结束
package day7; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; import java.util.Stack; /* * 最小生成树,返回边的集合 * kruskal : * 选边,从小到大(优先级队列),不形成回路(并查集),直到所有点都包括 || 所有边都遍历结束 */ public class Code05_Kruskal { //并查集 //两个map,一个father,head。 public static class UnionFind{ HashMap<Node, Node> fatherMap ; HashMap <Node ,Integer> sizeMap; public UnionFind() { fatherMap = new HashMap<Node, Node>(); sizeMap = new HashMap<Node, Integer>(); } public void inital (Collection<Node> nodes) { this.fatherMap.clear(); this.sizeMap.clear(); for (Node cur : nodes) { this.fatherMap.put(cur, cur); this.sizeMap.put(cur, 1); } } public boolean isSameSet(Node a, Node b) { return this.findFather1(a) == this.findFather1(b); } public void union(Node a, Node b) { Node fathera = findFather1(a); Node fatherb = findFather1(b); int sizea = this.sizeMap.get(fathera); int sizeb = this.sizeMap.get(fatherb); if (sizea <sizeb) { this.fatherMap.put(fathera, fatherb); this.sizeMap.put(fatherb, sizea +sizeb); }else { this.fatherMap.put(fatherb, fathera); this.sizeMap.put(fathera,sizea +sizeb ); } } //递归版本 public Node findFather2(Node cur) { if (!this.fatherMap.containsKey(cur)) return null; //get之前一定要记得判断null Node father = fatherMap.get(cur); while (father != cur) { father = findFather2(father); } this.fatherMap.put(cur, father); return father; } //非递归版本 public Node findFather1(Node cur) { if (!this.fatherMap.containsKey(cur)) return null; //get之前一定要记得判断null Node father = fatherMap.get(cur); Stack<Node> stack = new Stack<Node>(); while (cur != father) { stack.add(cur); cur = father; father = fatherMap.get(cur); } while (!stack.isEmpty()) { this.fatherMap.put(stack.pop(), father); } return father; } } public static class MyComaprator implements Comparator<Edge>{ @Override public int compare(Edge o1, Edge o2) { return o1.value -o2.value; } } public static Set<Edge> kruskal (Graph graph) { Queue<Edge> queue = new PriorityQueue<Edge>(new MyComaprator()); UnionFind union = new UnionFind(); union.inital(graph.nodes.values()); for (Edge edge : graph.edges) { queue.add(edge); } Set<Edge> result = new HashSet<Edge>(); while(!queue.isEmpty()) { Edge edge = queue.poll(); if(!union.isSameSet(edge.from, edge.to)) { result.add(edge); System.out.print(edge.value + " "); union.union(edge.from, edge.to); } } return result; } public static void main(String[] args) { int arr [][]= { {1,2,6},{1,3,1},{1,4,5},{2,3,5},{3,4,5}, {2,5,3},{3,5,6},{3,6,4},{4,6,2},{5,6,6}, // {2,1,6},{3,1,1},{4,1,5},{3,2,5},{4,3,5}, // {5,2,3},{5,3,6},{6,3,4},{6,4,2},{6,5,6}, }; // 无向图1 5 3 4 2 // 有向图 1 5 3 4 2 。默认无向图。因为是选边,不考虑方向 // int arr [][]= { // {1,2,1},{1,3,2},{2,4,3},{2,5,2},{4,6,4},}; Graph graph = new Graph(); graph.creatGraph(arr); kruskal(graph); } }
Prim:
- 从节点出发,查找相邻的边,
- 选择最小的边,查找边连接的节点是否出现过,
- 未出现,将边加入结果集,解锁新的节点(解锁新的边),新加入的边同原来的边一起重复2.
package day7; import java.util.Comparator; import java.util.HashSet; /* * 最小生成树:返回边的集合 * Prim:从点出发,查找相邻边,选择最小的,查看边相邻的节点是否出现过 * 未出现,加入result,解锁新变和原来的边一起,找最小的 * 出现继续 * */ import java.util.PriorityQueue; import java.util.Set; public class Code04_Prim { public static class MyComparator implements Comparator<Edge>{ @Override public int compare(Edge arg0, Edge arg1) { return arg0.value - arg1.value; } } public static Set<Edge> prim (Graph graph){ HashSet<Node> set = new HashSet<Node> (); PriorityQueue<Edge> queue = new PriorityQueue<Edge>(new MyComparator()); Set<Edge> result = new HashSet<Edge>(); //important for (Node node : graph.nodes.values()) { if (!set.contains(node)) { set.add(node); for (Edge edge : node.edge) { queue.add(edge); } while (!queue.isEmpty()) { Edge edge = queue.poll(); Node toNode = edge.to; if(!set.contains(toNode)) { set.add(toNode); //添加新点,从新点出发。 result.add(edge); System.out.println("from " +edge.from.value+" to "+edge.to.value+" vlaue "+ edge.value ); for (Edge nextedge : toNode.edge) { queue.add(nextedge); } } } } } return result; } public static void main(String[] args) { int arr [][]= { {1,2,6},{1,3,1},{1,4,5},{2,3,5},{3,4,5}, {2,5,3},{3,5,6},{3,6,4},{4,6,2},{5,6,6}, // {2,1,6},{3,1,1},{4,1,5},{3,2,5},{4,3,5}, // {5,2,3},{5,3,6},{6,3,4},{6,4,2},{6,5,6}, }; // 无向图1 5 3 4 2 // 有向图 1 4 5 6 3 // int arr [][]= { // {1,2,1},{1,3,2},{2,4,3},{2,5,2},{4,6,4},}; Graph graph = new Graph(); graph.creatGraph(arr); prim(graph); } }